From 1ed8b05169bc2eec2e66df4e43ffa13343756dbd Mon Sep 17 00:00:00 2001 From: Areloch Date: Fri, 24 Feb 2017 02:40:56 -0600 Subject: [PATCH] Initial implementation of the new Base Game Template and some starting modules. This makes some tweaks to the engine to support this, specifically, it tweaks the hardcoded shaderpaths to defer to a pref variable, so none of the shader paths are hardcoded. Also tweaks how post effects read in texture files, removing a bizzare filepath interpretation choice, where if the file path didn't start with "/" it forcefully appended the script's file path. This made it impossible to have images not in the same dir as the script file defining the post effect. This was changed and the existing template's post effects tweaked for now to just add "./" to those few paths impacted, as well as the perf vars to support the non-hardcoded shader paths in the engine. --- .../forest/glsl/windDeformationGLSL.cpp | 2 +- .../forest/hlsl/windDeformationHLSL.cpp | 2 +- Engine/source/gfx/D3D11/gfxD3D11Device.cpp | 16 +- Engine/source/gfx/D3D9/gfxD3D9Device.cpp | 16 +- Engine/source/gfx/gfxTextureManager.cpp | 6 +- Engine/source/gfx/gl/gfxGLDevice.cpp | 16 +- Engine/source/materials/shaderData.cpp | 12 +- Engine/source/postFx/postEffect.cpp | 4 - Engine/source/shaderGen/GLSL/bumpGLSL.cpp | 2 +- .../source/shaderGen/GLSL/pixSpecularGLSL.cpp | 2 +- .../shaderGen/GLSL/shaderFeatureGLSL.cpp | 14 +- .../source/shaderGen/GLSL/shaderGenGLSL.cpp | 4 +- Engine/source/shaderGen/HLSL/bumpHLSL.cpp | 2 +- .../source/shaderGen/HLSL/pixSpecularHLSL.cpp | 2 +- .../shaderGen/HLSL/shaderFeatureHLSL.cpp | 14 +- .../source/terrain/glsl/terrFeatureGLSL.cpp | 10 +- .../source/terrain/hlsl/terrFeatureHLSL.cpp | 10 +- Templates/BaseGame/BaseGame.cmake | 1 + Templates/BaseGame/DeleteCachedDTSs.bat | 1 + Templates/BaseGame/DeleteCachedDTSs.command | 15 + Templates/BaseGame/DeleteDSOs.bat | 6 + Templates/BaseGame/DeleteDSOs.command | 19 + Templates/BaseGame/DeletePrefs.bat | 6 + Templates/BaseGame/DeletePrefs.command | 3 + Templates/BaseGame/cleanShaders.bat | 7 + Templates/BaseGame/cleanShaders.command | 4 + .../BaseGame/game/Template.torsion.exports | 1 + Templates/BaseGame/game/core/audio.cs | 436 ++ Templates/BaseGame/game/core/canvas.cs | 162 + .../BaseGame/game/core/console/console.gui | 51 + Templates/BaseGame/game/core/console/main.cs | 108 + .../BaseGame/game/core/console/profiles.cs | 70 + Templates/BaseGame/game/core/cursor.cs | 102 + .../game/core/fonts/Arial 10 (ansi).uft | Bin 0 -> 412 bytes .../game/core/fonts/Arial 12 (ansi).uft | Bin 0 -> 62 bytes .../game/core/fonts/Arial 14 (ansi).uft | Bin 0 -> 5123 bytes .../game/core/fonts/Arial 16 (ansi).uft | Bin 0 -> 13112 bytes .../game/core/fonts/Arial 36 (ansi).uft | Bin 0 -> 1046 bytes .../game/core/fonts/Arial Bold 14 (ansi).uft | Bin 0 -> 3227 bytes .../game/core/fonts/Arial Bold 16 (ansi).uft | Bin 0 -> 1254 bytes .../game/core/fonts/Arial Bold 18 (ansi).uft | Bin 0 -> 5132 bytes .../game/core/fonts/ArialBold 14 (ansi).uft | Bin 0 -> 2172 bytes .../game/core/fonts/ArialItalic 14 (ansi).uft | Bin 0 -> 2876 bytes .../core/fonts/Lucida Console 12 (ansi).uft | Bin 0 -> 5850 bytes .../BaseGame/game/core/gfxData/clouds.cs | 55 + .../game/core/gfxData/commonMaterialData.cs | 79 + .../BaseGame/game/core/gfxData/scatterSky.cs | 48 + .../BaseGame/game/core/gfxData/shaders.cs | 140 + .../game/core/gfxData/terrainBlock.cs | 36 + Templates/BaseGame/game/core/gfxData/water.cs | 208 + .../gfxprofile/D3D9.ATITechnologiesInc.cs | 36 + .../gfxprofile/D3D9.NVIDIA.GeForce8600.cs | 36 + .../gfxprofile/D3D9.NVIDIA.QuadroFXGo1000.cs | 39 + .../game/core/gfxprofile/D3D9.NVIDIA.cs | 36 + .../BaseGame/game/core/gfxprofile/D3D9.cs | 26 + Templates/BaseGame/game/core/globals.cs | 102 + .../BaseGame/game/core/helperFunctions.cs | 528 ++ .../BaseGame/game/core/images/button.png | Bin 0 -> 1153 bytes .../BaseGame/game/core/images/checkbox.png | Bin 0 -> 3943 bytes .../game/core/images/group-border.png | Bin 0 -> 1273 bytes .../game/core/images/inactive-overlay.png | Bin 0 -> 131 bytes .../BaseGame/game/core/images/loadingbar.png | Bin 0 -> 635 bytes .../BaseGame/game/core/images/materials.cs | 39 + .../game/core/images/missingTexture.png | Bin 0 -> 10645 bytes .../BaseGame/game/core/images/scrollBar.png | Bin 0 -> 3332 bytes .../BaseGame/game/core/images/textEdit.png | Bin 0 -> 250 bytes .../game/core/images/thumbHighlightButton.png | Bin 0 -> 778 bytes .../BaseGame/game/core/images/unavailable.png | Bin 0 -> 26528 bytes .../BaseGame/game/core/images/warnMat.dds | Bin 0 -> 699192 bytes .../BaseGame/game/core/images/window.png | Bin 0 -> 2559 bytes Templates/BaseGame/game/core/lighting.cs | 74 + .../core/lighting/advanced/deferredShading.cs | 147 + .../game/core/lighting/advanced/depthviz.png | Bin 0 -> 551 bytes .../game/core/lighting/advanced/init.cs | 80 + .../game/core/lighting/advanced/lightViz.cs | 294 + .../game/core/lighting/advanced/shaders.cs | 276 + .../game/core/lighting/advanced/shadowViz.cs | 116 + .../game/core/lighting/advanced/shadowViz.gui | 78 + .../BaseGame/game/core/lighting/basic/init.cs | 92 + .../game/core/lighting/basic/shadowFilter.cs | 76 + .../game/core/lighting/shadowMaps/init.cs | 32 + Templates/BaseGame/game/core/main.cs | 95 + Templates/BaseGame/game/core/parseArgs.cs | 392 ++ Templates/BaseGame/game/core/postFx.cs | 63 + Templates/BaseGame/game/core/profiles.cs | 226 + Templates/BaseGame/game/core/renderManager.cs | 125 + .../BaseGame/game/core/sfx/audioAmbience.cs | 44 + Templates/BaseGame/game/core/sfx/audioData.cs | 42 + .../game/core/sfx/audioDescriptions.cs | 143 + .../game/core/sfx/audioEnvironments.cs | 916 +++ .../BaseGame/game/core/sfx/audioStates.cs | 158 + .../game/data/clientServer/ClientServer.cs | 112 + .../data/clientServer/ClientServer.module | 9 + .../clientServer/scripts/client/client.cs | 29 + .../scripts/client/connectionToServer.cs | 130 + .../scripts/client/levelDownload.cs | 185 + .../clientServer/scripts/client/levelLoad.cs | 92 + .../clientServer/scripts/client/message.cs | 94 + .../data/clientServer/scripts/server/audio.cs | 40 + .../clientServer/scripts/server/commands.cs | 35 + .../scripts/server/connectionToClient.cs | 161 + .../clientServer/scripts/server/defaults.cs | 62 + .../scripts/server/levelDownload.cs | 171 + .../clientServer/scripts/server/levelInfo.cs | 197 + .../clientServer/scripts/server/levelLoad.cs | 181 + .../clientServer/scripts/server/message.cs | 50 + .../clientServer/scripts/server/server.cs | 291 + Templates/BaseGame/game/data/defaults.cs | 218 + .../game/data/postFX/art/AreaMap33.dds | Bin 0 -> 109028 bytes .../game/data/postFX/art/caustics_1.png | Bin 0 -> 34398 bytes .../game/data/postFX/art/caustics_2.png | Bin 0 -> 33963 bytes .../BaseGame/game/data/postFX/art/noise.png | Bin 0 -> 14610 bytes .../game/data/postFX/art/null_color_ramp.png | Bin 0 -> 2843 bytes Templates/BaseGame/game/data/postFX/postFX.cs | 45 + .../BaseGame/game/data/postFX/postFX.module | 9 + .../data/postFX/scripts/client/GammaPostFX.cs | 74 + .../game/data/postFX/scripts/client/MLAA.cs | 186 + .../postFX/scripts/client/MotionBlurFx.cs | 53 + .../data/postFX/scripts/client/caustics.cs | 64 + .../postFX/scripts/client/chromaticLens.cs | 77 + .../scripts/client/default.postfxpreset.cs | 72 + .../game/data/postFX/scripts/client/dof.cs | 599 ++ .../game/data/postFX/scripts/client/edgeAA.cs | 113 + .../game/data/postFX/scripts/client/flash.cs | 63 + .../game/data/postFX/scripts/client/fog.cs | 135 + .../game/data/postFX/scripts/client/fxaa.cs | 64 + .../game/data/postFX/scripts/client/glow.cs | 184 + .../game/data/postFX/scripts/client/hdr.cs | 533 ++ .../data/postFX/scripts/client/lightRay.cs | 110 + .../scripts/client/ovrBarrelDistortion.cs | 167 + .../scripts/client/postFxManager.gui.cs | 446 ++ .../client/postFxManager.gui.settings.cs | 439 ++ .../client/postFxManager.persistance.cs | 79 + .../game/data/postFX/scripts/client/ssao.cs | 302 + .../data/postFX/scripts/client/turbulence.cs | 57 + .../data/postFX/scripts/client/vignette.cs | 55 + .../data/postFX/scripts/gui/postFxManager.gui | 2755 +++++++++ .../shaders/common/VolumetricFog/VFogP.hlsl | 87 + .../common/VolumetricFog/VFogPreP.hlsl | 40 + .../common/VolumetricFog/VFogPreV.hlsl | 44 + .../common/VolumetricFog/VFogRefl.hlsl | 38 + .../shaders/common/VolumetricFog/VFogV.hlsl | 46 + .../common/VolumetricFog/gl/VFogP.glsl | 87 + .../common/VolumetricFog/gl/VFogPreP.glsl | 37 + .../common/VolumetricFog/gl/VFogPreV.glsl | 42 + .../common/VolumetricFog/gl/VFogRefl.glsl | 33 + .../common/VolumetricFog/gl/VFogV.glsl | 38 + .../data/shaders/common/basicCloudsP.hlsl | 37 + .../data/shaders/common/basicCloudsV.hlsl | 58 + .../game/data/shaders/common/cloudLayerP.hlsl | 146 + .../game/data/shaders/common/cloudLayerV.hlsl | 106 + .../fixedFunction/addColorTextureP.hlsl | 37 + .../fixedFunction/addColorTextureV.hlsl | 48 + .../shaders/common/fixedFunction/colorP.hlsl | 34 + .../shaders/common/fixedFunction/colorV.hlsl | 45 + .../fixedFunction/gl/addColorTextureP.glsl | 32 + .../fixedFunction/gl/addColorTextureV.glsl | 38 + .../common/fixedFunction/gl/colorP.glsl | 30 + .../common/fixedFunction/gl/colorV.glsl | 35 + .../fixedFunction/gl/modColorTextureP.glsl | 32 + .../fixedFunction/gl/modColorTextureV.glsl | 38 + .../fixedFunction/gl/targetRestoreP.glsl | 31 + .../fixedFunction/gl/targetRestoreV.glsl | 22 + .../common/fixedFunction/gl/textureP.glsl | 31 + .../common/fixedFunction/gl/textureV.glsl | 35 + .../fixedFunction/modColorTextureP.hlsl | 37 + .../fixedFunction/modColorTextureV.hlsl | 48 + .../common/fixedFunction/targetRestoreP.hlsl | 31 + .../common/fixedFunction/targetRestoreV.hlsl | 26 + .../common/fixedFunction/textureP.hlsl | 36 + .../common/fixedFunction/textureV.hlsl | 46 + .../game/data/shaders/common/foliage.hlsl | 186 + .../shaders/common/fxFoliageReplicatorP.hlsl | 60 + .../shaders/common/fxFoliageReplicatorV.hlsl | 129 + .../data/shaders/common/gl/basicCloudsP.glsl | 39 + .../data/shaders/common/gl/basicCloudsV.glsl | 53 + .../game/data/shaders/common/gl/blurP.glsl | 39 + .../game/data/shaders/common/gl/blurV.glsl | 48 + .../data/shaders/common/gl/cloudLayerP.glsl | 147 + .../data/shaders/common/gl/cloudLayerV.glsl | 106 + .../game/data/shaders/common/gl/foliage.glsl | 186 + .../common/gl/fxFoliageReplicatorP.glsl | 42 + .../common/gl/fxFoliageReplicatorV.glsl | 99 + .../data/shaders/common/gl/guiMaterialV.glsl | 39 + .../data/shaders/common/gl/hlslCompat.glsl | 103 + .../game/data/shaders/common/gl/imposter.glsl | 161 + .../game/data/shaders/common/gl/lighting.glsl | 249 + .../shaders/common/gl/particleCompositeP.glsl | 62 + .../shaders/common/gl/particleCompositeV.glsl | 48 + .../data/shaders/common/gl/particlesP.glsl | 113 + .../data/shaders/common/gl/particlesV.glsl | 54 + .../shaders/common/gl/planarReflectBumpP.glsl | 70 + .../shaders/common/gl/planarReflectBumpV.glsl | 51 + .../shaders/common/gl/planarReflectP.glsl | 43 + .../shaders/common/gl/planarReflectV.glsl | 51 + .../game/data/shaders/common/gl/precipP.glsl | 39 + .../game/data/shaders/common/gl/precipV.glsl | 54 + .../shaders/common/gl/projectedShadowP.glsl | 37 + .../shaders/common/gl/projectedShadowV.glsl | 49 + .../data/shaders/common/gl/scatterSkyP.glsl | 72 + .../data/shaders/common/gl/scatterSkyV.glsl | 154 + .../game/data/shaders/common/gl/torque.glsl | 339 ++ .../game/data/shaders/common/gl/wavesP.glsl | 57 + .../game/data/shaders/common/gl/wind.glsl | 101 + .../data/shaders/common/guiMaterialV.hlsl | 45 + .../game/data/shaders/common/hlslStructs.h | 116 + .../game/data/shaders/common/hlslStructs.hlsl | 114 + .../game/data/shaders/common/imposter.hlsl | 149 + .../game/data/shaders/common/lighting.hlsl | 249 + .../lighting/advanced/convexGeometryV.hlsl | 54 + .../lighting/advanced/dbgColorBufferP.hlsl | 30 + .../lighting/advanced/dbgDepthVisualizeP.hlsl | 33 + .../lighting/advanced/dbgGlowVisualizeP.hlsl | 30 + .../advanced/dbgLightColorVisualizeP.hlsl | 32 + .../advanced/dbgLightSpecularVisualizeP.hlsl | 31 + .../advanced/dbgNormalVisualizeP.hlsl | 32 + .../advanced/dbgShadowVisualizeP.hlsl | 38 + .../advanced/dbgSpecMapVisualizeP.hlsl | 31 + .../advanced/deferredClearGBufferP.hlsl | 54 + .../advanced/deferredClearGBufferV.hlsl | 43 + .../advanced/deferredColorShaderP.hlsl | 46 + .../lighting/advanced/deferredShadingP.hlsl | 54 + .../lighting/advanced/farFrustumQuad.hlsl | 47 + .../lighting/advanced/farFrustumQuadV.hlsl | 43 + .../lighting/advanced/gl/convexGeometryV.glsl | 52 + .../lighting/advanced/gl/dbgColorBufferP.glsl | 33 + .../advanced/gl/dbgDepthVisualizeP.glsl | 36 + .../advanced/gl/dbgGlowVisualizeP.glsl | 33 + .../advanced/gl/dbgLightColorVisualizeP.glsl | 34 + .../gl/dbgLightSpecularVisualizeP.glsl | 34 + .../advanced/gl/dbgNormalVisualizeP.glsl | 35 + .../advanced/gl/dbgShadowVisualizeP.glsl | 34 + .../advanced/gl/dbgSpecMapVisualizeP.glsl | 33 + .../advanced/gl/deferredClearGBufferP.glsl | 40 + .../advanced/gl/deferredColorShaderP.glsl | 37 + .../advanced/gl/deferredShadingP.glsl | 59 + .../lighting/advanced/gl/farFrustumQuad.glsl | 30 + .../lighting/advanced/gl/farFrustumQuadV.glsl | 51 + .../lighting/advanced/gl/lightingUtils.glsl | 79 + .../lighting/advanced/gl/pointLightP.glsl | 273 + .../lighting/advanced/gl/softShadow.glsl | 159 + .../lighting/advanced/gl/spotLightP.glsl | 210 + .../lighting/advanced/gl/vectorLightP.glsl | 327 ++ .../lighting/advanced/lightingUtils.hlsl | 51 + .../advanced/particlePointLightP.hlsl | 76 + .../advanced/particlePointLightV.hlsl | 48 + .../common/lighting/advanced/pointLightP.hlsl | 277 + .../common/lighting/advanced/softShadow.hlsl | 158 + .../common/lighting/advanced/spotLightP.hlsl | 209 + .../lighting/advanced/vectorLightP.hlsl | 328 ++ .../lighting/basic/gl/shadowFilterP.glsl | 46 + .../lighting/basic/gl/shadowFilterV.glsl | 37 + .../common/lighting/basic/shadowFilterP.hlsl | 50 + .../common/lighting/basic/shadowFilterV.hlsl | 42 + .../common/lighting/shadowMap/boxFilterP.hlsl | 82 + .../common/lighting/shadowMap/boxFilterV.hlsl | 57 + .../lighting/shadowMap/gl/boxFilterP.glsl | 49 + .../lighting/shadowMap/gl/boxFilterV.glsl | 34 + .../common/lighting/shadowMap/shadowMapIO.h | 50 + .../lighting/shadowMap/shadowMapIO_GLSL.h | 50 + .../lighting/shadowMap/shadowMapIO_HLSL.h | 50 + .../shaders/common/particleCompositeP.hlsl | 61 + .../shaders/common/particleCompositeV.hlsl | 53 + .../game/data/shaders/common/particlesP.hlsl | 109 + .../game/data/shaders/common/particlesV.hlsl | 55 + .../shaders/common/planarReflectBumpP.hlsl | 87 + .../shaders/common/planarReflectBumpV.hlsl | 67 + .../data/shaders/common/planarReflectP.hlsl | 58 + .../data/shaders/common/planarReflectV.hlsl | 57 + .../shaders/common/postFx/VolFogGlowP.hlsl | 74 + .../common/postFx/caustics/causticsP.hlsl | 77 + .../common/postFx/caustics/gl/causticsP.glsl | 87 + .../shaders/common/postFx/chromaticLens.hlsl | 60 + .../common/postFx/dof/DOF_CalcCoC_P.hlsl | 53 + .../common/postFx/dof/DOF_CalcCoC_V.hlsl | 70 + .../common/postFx/dof/DOF_DownSample_P.hlsl | 143 + .../common/postFx/dof/DOF_DownSample_V.hlsl | 61 + .../common/postFx/dof/DOF_Final_P.hlsl | 145 + .../common/postFx/dof/DOF_Final_V.hlsl | 72 + .../common/postFx/dof/DOF_Gausian_P.hlsl | 63 + .../common/postFx/dof/DOF_Gausian_V.hlsl | 80 + .../common/postFx/dof/DOF_Passthrough_V.hlsl | 70 + .../common/postFx/dof/DOF_SmallBlur_P.hlsl | 46 + .../common/postFx/dof/DOF_SmallBlur_V.hlsl | 56 + .../common/postFx/dof/gl/DOF_CalcCoC_P.glsl | 55 + .../common/postFx/dof/gl/DOF_CalcCoC_V.glsl | 69 + .../postFx/dof/gl/DOF_DownSample_P.glsl | 143 + .../postFx/dof/gl/DOF_DownSample_V.glsl | 67 + .../common/postFx/dof/gl/DOF_Final_P.glsl | 147 + .../common/postFx/dof/gl/DOF_Final_V.glsl | 71 + .../common/postFx/dof/gl/DOF_Gausian_P.glsl | 68 + .../common/postFx/dof/gl/DOF_Gausian_V.glsl | 91 + .../postFx/dof/gl/DOF_Passthrough_V.glsl | 69 + .../common/postFx/dof/gl/DOF_SmallBlur_P.glsl | 46 + .../common/postFx/dof/gl/DOF_SmallBlur_V.glsl | 54 + .../common/postFx/edgeaa/dbgEdgeDisplayP.hlsl | 30 + .../shaders/common/postFx/edgeaa/edgeAAP.hlsl | 66 + .../shaders/common/postFx/edgeaa/edgeAAV.hlsl | 45 + .../common/postFx/edgeaa/edgeDetectP.hlsl | 93 + .../postFx/edgeaa/gl/dbgEdgeDisplayP.glsl | 36 + .../common/postFx/edgeaa/gl/edgeAAP.glsl | 70 + .../common/postFx/edgeaa/gl/edgeAAV.glsl | 43 + .../common/postFx/edgeaa/gl/edgeDetectP.glsl | 96 + .../data/shaders/common/postFx/flashP.hlsl | 36 + .../game/data/shaders/common/postFx/fogP.hlsl | 47 + .../shaders/common/postFx/fxaa/Fxaa3_11.h | 2047 +++++++ .../shaders/common/postFx/fxaa/fxaaP.hlsl | 143 + .../shaders/common/postFx/fxaa/fxaaV.hlsl | 42 + .../shaders/common/postFx/fxaa/gl/fxaaP.glsl | 125 + .../shaders/common/postFx/fxaa/gl/fxaaV.glsl | 40 + .../data/shaders/common/postFx/gammaP.hlsl | 53 + .../shaders/common/postFx/gl/VolFogGlowP.glsl | 67 + .../common/postFx/gl/chromaticLens.glsl | 62 + .../data/shaders/common/postFx/gl/flashP.glsl | 39 + .../data/shaders/common/postFx/gl/fogP.glsl | 52 + .../data/shaders/common/postFx/gl/gammaP.glsl | 57 + .../shaders/common/postFx/gl/glowBlurP.glsl | 59 + .../shaders/common/postFx/gl/glowBlurV.glsl | 59 + .../shaders/common/postFx/gl/motionBlurP.glsl | 78 + .../shaders/common/postFx/gl/passthruP.glsl | 33 + .../data/shaders/common/postFx/gl/postFX.glsl | 63 + .../shaders/common/postFx/gl/postFxV.glsl | 52 + .../shaders/common/postFx/gl/turbulenceP.glsl | 52 + .../common/postFx/gl/underwaterFogP.glsl | 140 + .../data/shaders/common/postFx/glowBlurP.hlsl | 63 + .../data/shaders/common/postFx/glowBlurV.hlsl | 63 + .../common/postFx/hdr/bloomGaussBlurHP.hlsl | 68 + .../common/postFx/hdr/bloomGaussBlurVP.hlsl | 67 + .../common/postFx/hdr/brightPassFilterP.hlsl | 62 + .../postFx/hdr/calculateAdaptedLumP.hlsl | 44 + .../common/postFx/hdr/downScale4x4P.hlsl | 53 + .../common/postFx/hdr/downScale4x4V.hlsl | 138 + .../common/postFx/hdr/finalPassCombineP.hlsl | 101 + .../postFx/hdr/gl/bloomGaussBlurHP.glsl | 72 + .../postFx/hdr/gl/bloomGaussBlurVP.glsl | 71 + .../postFx/hdr/gl/brightPassFilterP.glsl | 65 + .../postFx/hdr/gl/calculateAdaptedLumP.glsl | 48 + .../common/postFx/hdr/gl/downScale4x4P.glsl | 50 + .../common/postFx/hdr/gl/downScale4x4V.glsl | 141 + .../postFx/hdr/gl/finalPassCombineP.glsl | 106 + .../common/postFx/hdr/gl/luminanceVisP.glsl | 42 + .../postFx/hdr/gl/sampleLumInitialP.glsl | 62 + .../postFx/hdr/gl/sampleLumIterativeP.glsl | 52 + .../common/postFx/hdr/luminanceVisP.hlsl | 39 + .../common/postFx/hdr/sampleLumInitialP.hlsl | 59 + .../postFx/hdr/sampleLumIterativeP.hlsl | 50 + .../postFx/lightRay/gl/lightRayOccludeP.glsl | 55 + .../common/postFx/lightRay/gl/lightRayP.glsl | 94 + .../postFx/lightRay/lightRayOccludeP.hlsl | 53 + .../common/postFx/lightRay/lightRayP.hlsl | 89 + .../postFx/mlaa/blendWeightCalculationP.hlsl | 78 + .../common/postFx/mlaa/edgeDetectionP.hlsl | 72 + .../shaders/common/postFx/mlaa/functions.hlsl | 145 + .../mlaa/gl/blendWeightCalculationP.glsl | 83 + .../common/postFx/mlaa/gl/edgeDetectionP.glsl | 76 + .../common/postFx/mlaa/gl/functions.glsl | 145 + .../postFx/mlaa/gl/neighborhoodBlendingP.glsl | 88 + .../common/postFx/mlaa/gl/offsetV.glsl | 57 + .../common/postFx/mlaa/gl/passthruV.glsl | 52 + .../postFx/mlaa/neighborhoodBlendingP.hlsl | 84 + .../shaders/common/postFx/mlaa/offsetV.hlsl | 42 + .../shaders/common/postFx/mlaa/passthruV.hlsl | 37 + .../shaders/common/postFx/motionBlurP.hlsl | 70 + .../oculusvr/barrelDistortionChromaP.hlsl | 95 + .../postFx/oculusvr/barrelDistortionP.hlsl | 80 + .../oculusvr/gl/barrelDistortionChromaP.glsl | 95 + .../postFx/oculusvr/gl/barrelDistortionP.glsl | 81 + .../postFx/oculusvr/gl/monoToStereoP.glsl | 60 + .../common/postFx/oculusvr/monoToStereoP.hlsl | 59 + .../data/shaders/common/postFx/passthruP.hlsl | 30 + .../data/shaders/common/postFx/postFx.hlsl | 40 + .../data/shaders/common/postFx/postFxV.glsl | 51 + .../data/shaders/common/postFx/postFxV.hlsl | 45 + .../common/postFx/ssao/SSAO_Blur_P.hlsl | 106 + .../common/postFx/ssao/SSAO_Blur_V.hlsl | 86 + .../shaders/common/postFx/ssao/SSAO_P.hlsl | 272 + .../common/postFx/ssao/SSAO_PowerTable_P.hlsl | 29 + .../common/postFx/ssao/SSAO_PowerTable_V.hlsl | 45 + .../common/postFx/ssao/gl/SSAO_Blur_P.glsl | 108 + .../common/postFx/ssao/gl/SSAO_Blur_V.glsl | 96 + .../shaders/common/postFx/ssao/gl/SSAO_P.glsl | 278 + .../postFx/ssao/gl/SSAO_PowerTable_P.glsl | 34 + .../postFx/ssao/gl/SSAO_PowerTable_V.glsl | 38 + .../shaders/common/postFx/turbulenceP.hlsl | 44 + .../shaders/common/postFx/underwaterFogP.hlsl | 138 + .../common/postFx/vignette/VignetteP.hlsl | 35 + .../common/postFx/vignette/gl/VignetteP.glsl | 41 + .../game/data/shaders/common/precipP.hlsl | 53 + .../game/data/shaders/common/precipV.hlsl | 71 + .../data/shaders/common/projectedShadowP.hlsl | 40 + .../data/shaders/common/projectedShadowV.hlsl | 60 + .../common/ribbons/basicRibbonShaderP.hlsl | 19 + .../common/ribbons/basicRibbonShaderV.hlsl | 35 + .../common/ribbons/gl/basicRibbonShaderP.glsl | 20 + .../common/ribbons/gl/basicRibbonShaderV.glsl | 37 + .../common/ribbons/gl/texRibbonShaderP.glsl | 22 + .../common/ribbons/gl/texRibbonShaderV.glsl | 37 + .../common/ribbons/texRibbonShaderP.hlsl | 21 + .../common/ribbons/texRibbonShaderV.hlsl | 35 + .../game/data/shaders/common/scatterSkyP.hlsl | 69 + .../game/data/shaders/common/scatterSkyV.hlsl | 157 + .../game/data/shaders/common/shaderModel.hlsl | 97 + .../shaders/common/shaderModelAutoGen.hlsl | 35 + .../game/data/shaders/common/shdrConsts.h | 117 + .../data/shaders/common/terrain/blendP.hlsl | 48 + .../data/shaders/common/terrain/blendV.hlsl | 52 + .../shaders/common/terrain/gl/blendP.glsl | 48 + .../shaders/common/terrain/gl/blendV.glsl | 41 + .../data/shaders/common/terrain/terrain.glsl | 52 + .../data/shaders/common/terrain/terrain.hlsl | 55 + .../game/data/shaders/common/torque.hlsl | 342 ++ .../shaders/common/water/gl/waterBasicP.glsl | 217 + .../shaders/common/water/gl/waterBasicV.glsl | 243 + .../data/shaders/common/water/gl/waterP.glsl | 397 ++ .../data/shaders/common/water/gl/waterV.glsl | 241 + .../shaders/common/water/waterBasicP.hlsl | 214 + .../shaders/common/water/waterBasicV.hlsl | 237 + .../data/shaders/common/water/waterP.hlsl | 384 ++ .../data/shaders/common/water/waterV.hlsl | 216 + .../game/data/shaders/common/wavesP.hlsl | 89 + .../game/data/shaders/common/wavesV.hlsl | 90 + .../game/data/shaders/common/wind.hlsl | 101 + Templates/BaseGame/game/data/splash.png | Bin 0 -> 11864 bytes Templates/BaseGame/game/data/torque.png | Bin 0 -> 1331 bytes Templates/BaseGame/game/data/ui/UI.cs | 63 + Templates/BaseGame/game/data/ui/UI.module | 9 + .../data/ui/art/ScreenBrightness_Dark.png | Bin 0 -> 18962 bytes .../data/ui/art/ScreenBrightness_Light.png | Bin 0 -> 17371 bytes .../data/ui/art/Torque-3D-logo-shortcut.png | Bin 0 -> 10728 bytes .../game/data/ui/art/Torque-3D-logo-w.png | Bin 0 -> 19328 bytes .../game/data/ui/art/Torque-3D-logo.png | Bin 0 -> 9063 bytes .../game/data/ui/art/Torque-3D-logo_alt.png | Bin 0 -> 11616 bytes .../game/data/ui/art/background-dark.png | Bin 0 -> 2350 bytes .../BaseGame/game/data/ui/art/background.png | Bin 0 -> 2466 bytes .../BaseGame/game/data/ui/art/buttontab.png | Bin 0 -> 659 bytes .../game/data/ui/art/chatHudBorderArray.png | Bin 0 -> 1176 bytes .../BaseGame/game/data/ui/art/checkbox.png | Bin 0 -> 3943 bytes .../BaseGame/game/data/ui/art/clear-btn_d.png | Bin 0 -> 593 bytes .../BaseGame/game/data/ui/art/clear-btn_h.png | Bin 0 -> 595 bytes .../BaseGame/game/data/ui/art/clear-btn_n.png | Bin 0 -> 377 bytes .../game/data/ui/art/collapse-toolbar_d.png | Bin 0 -> 280 bytes .../game/data/ui/art/collapse-toolbar_h.png | Bin 0 -> 468 bytes .../game/data/ui/art/collapse-toolbar_n.png | Bin 0 -> 439 bytes .../game/data/ui/art/defaultCursor.png | Bin 0 -> 615 bytes .../BaseGame/game/data/ui/art/dropDown.png | Bin 0 -> 1309 bytes .../data/ui/art/dropdown-button-arrow.png | Bin 0 -> 132 bytes .../game/data/ui/art/dropdown-textEdit.png | Bin 0 -> 390 bytes .../game/data/ui/art/dropslider_d.png | Bin 0 -> 433 bytes .../game/data/ui/art/dropslider_h.png | Bin 0 -> 431 bytes .../game/data/ui/art/dropslider_n.png | Bin 0 -> 428 bytes .../game/data/ui/art/expand-toolbar_d.png | Bin 0 -> 278 bytes .../game/data/ui/art/expand-toolbar_h.png | Bin 0 -> 468 bytes .../game/data/ui/art/expand-toolbar_n.png | Bin 0 -> 437 bytes .../BaseGame/game/data/ui/art/folder.png | Bin 0 -> 236 bytes .../game/data/ui/art/group-border.png | Bin 0 -> 1273 bytes .../BaseGame/game/data/ui/art/hudfill.png | Bin 0 -> 126 bytes .../game/data/ui/art/inactive-overlay.png | Bin 0 -> 131 bytes .../BaseGame/game/data/ui/art/lagIcon.png | Bin 0 -> 2941 bytes .../BaseGame/game/data/ui/art/loadingbar.png | Bin 0 -> 635 bytes .../BaseGame/game/data/ui/art/macCursor.png | Bin 0 -> 458 bytes .../BaseGame/game/data/ui/art/menu-button.png | Bin 0 -> 1493 bytes Templates/BaseGame/game/data/ui/art/menu.png | Bin 0 -> 1362 bytes .../BaseGame/game/data/ui/art/menuSlider.png | Bin 0 -> 908 bytes Templates/BaseGame/game/data/ui/art/new_d.png | Bin 0 -> 200 bytes Templates/BaseGame/game/data/ui/art/new_h.png | Bin 0 -> 200 bytes Templates/BaseGame/game/data/ui/art/new_n.png | Bin 0 -> 200 bytes .../game/data/ui/art/next-button_d.png | Bin 0 -> 279 bytes .../game/data/ui/art/next-button_h.png | Bin 0 -> 549 bytes .../game/data/ui/art/next-button_n.png | Bin 0 -> 484 bytes .../BaseGame/game/data/ui/art/no-preview.png | Bin 0 -> 34615 bytes .../game/data/ui/art/numericslider.png | Bin 0 -> 993 bytes .../game/data/ui/art/previous-button_d.png | Bin 0 -> 290 bytes .../game/data/ui/art/previous-button_h.png | Bin 0 -> 561 bytes .../game/data/ui/art/previous-button_n.png | Bin 0 -> 494 bytes .../BaseGame/game/data/ui/art/radioButton.png | Bin 0 -> 843 bytes .../BaseGame/game/data/ui/art/scrollBar.png | Bin 0 -> 3332 bytes .../data/ui/art/selector-button-blank.png | Bin 0 -> 744 bytes .../game/data/ui/art/selector-button-dark.png | Bin 0 -> 1942 bytes .../ui/art/selector-button-highlight-only.png | Bin 0 -> 1613 bytes .../game/data/ui/art/selector-button.png | Bin 0 -> 4002 bytes .../BaseGame/game/data/ui/art/separator-h.png | Bin 0 -> 117 bytes .../BaseGame/game/data/ui/art/separator-v.png | Bin 0 -> 118 bytes .../game/data/ui/art/slider-w-box.png | Bin 0 -> 982 bytes .../BaseGame/game/data/ui/art/slider.png | Bin 0 -> 908 bytes .../BaseGame/game/data/ui/art/tab-border.png | Bin 0 -> 1203 bytes Templates/BaseGame/game/data/ui/art/tab.png | Bin 0 -> 939 bytes .../BaseGame/game/data/ui/art/textEdit.png | Bin 0 -> 250 bytes .../game/data/ui/art/textEditSliderBox.png | Bin 0 -> 226 bytes .../BaseGame/game/data/ui/art/window.png | Bin 0 -> 2559 bytes .../game/data/ui/scripts/chooseLevelDlg.cs | 301 + .../game/data/ui/scripts/controlsMenu.cs | 417 ++ .../data/ui/scripts/datablocks/guiSounds.cs | 34 + .../game/data/ui/scripts/graphicsMenu.cs | 567 ++ .../data/ui/scripts/guis/RecordingsDlg.gui | 230 + .../data/ui/scripts/guis/chooseLevelDlg.gui | 287 + .../ui/scripts/guis/controlsMenuSetting.taml | 102 + .../guis/graphicsMenuSettingsCtrl.taml | 159 + .../guis/graphicsMenuSettingsSlider.taml | 140 + .../data/ui/scripts/guis/joinServerMenu.gui | 455 ++ .../game/data/ui/scripts/guis/loadingGui.gui | 102 + .../game/data/ui/scripts/guis/mainMenu.gui | 194 + .../data/ui/scripts/guis/messageBoxOK.ed.gui | 60 + .../data/ui/scripts/guis/messageBoxYesNo.gui | 126 + .../game/data/ui/scripts/guis/netGraphGui.gui | 557 ++ .../game/data/ui/scripts/guis/optionsDlg.gui | 1417 +++++ .../game/data/ui/scripts/guis/optionsMenu.gui | 5194 +++++++++++++++++ .../game/data/ui/scripts/guis/pauseMenu.gui | 153 + .../game/data/ui/scripts/guis/profiler.gui | 661 +++ .../data/ui/scripts/guis/remapConfirmDlg.gui | 125 + .../game/data/ui/scripts/guis/remapDlg.gui | 122 + .../game/data/ui/scripts/joinServerMenu.cs | 143 + .../BaseGame/game/data/ui/scripts/mainMenu.cs | 41 + .../game/data/ui/scripts/messageBoxes.cs | 301 + .../game/data/ui/scripts/optionsList.cs | 437 ++ .../game/data/ui/scripts/optionsMenu.cs | 473 ++ .../game/data/ui/scripts/pauseMenu.cs | 30 + .../BaseGame/game/data/ui/scripts/profiles.cs | 429 ++ .../game/data/ui/sound/buttonClick.wav | Bin 0 -> 16596 bytes .../game/data/ui/sound/buttonHover.wav | Bin 0 -> 40136 bytes Templates/BaseGame/game/main.cs.in | 93 + Templates/BaseGame/game/runTests.cs | 5 + .../game/tools/base/canvas/baseCanvas.ed.cs | 55 + Templates/BaseGame/game/tools/base/main.cs | 35 + .../game/tools/base/menuBar/baseMenu.ed.cs | 81 + .../game/tools/base/menuBar/fileMenu.ed.cs | 52 + .../game/tools/base/menuBar/menuBuilder.ed.cs | 248 + .../game/tools/base/utils/inspector.ed.cs | 289 + .../base/utils/objectNameValidation.ed.cs | 59 + .../game/tools/base/utils/swatchButtons.ed.cs | 99 + .../base/utils/treeViewFilterCtrls.ed.cs | 74 + .../game/tools/base/utils/undoActions.ed.cs | 93 + .../game/tools/classIcons/BasicClouds.png | Bin 0 -> 502 bytes .../BaseGame/game/tools/classIcons/Camera.png | Bin 0 -> 526 bytes .../game/tools/classIcons/CameraBookmark.png | Bin 0 -> 526 bytes .../game/tools/classIcons/CloudLayer.png | Bin 0 -> 652 bytes .../game/tools/classIcons/ConvexShape.png | Bin 0 -> 374 bytes .../game/tools/classIcons/CreatorTree.png | Bin 0 -> 196 bytes .../game/tools/classIcons/DecalRoad.png | Bin 0 -> 514 bytes .../BaseGame/game/tools/classIcons/Forest.png | Bin 0 -> 800 bytes .../game/tools/classIcons/ForestBrush.png | Bin 0 -> 710 bytes .../tools/classIcons/ForestBrushElement.png | Bin 0 -> 409 bytes .../game/tools/classIcons/GameTSCtrl.png | Bin 0 -> 558 bytes .../game/tools/classIcons/GroundCover.png | Bin 0 -> 368 bytes .../game/tools/classIcons/GroundPlane.png | Bin 0 -> 132 bytes .../tools/classIcons/GuiAutoScrollCtrl.png | Bin 0 -> 252 bytes .../tools/classIcons/GuiBitmapBorderCtrl.png | Bin 0 -> 480 bytes .../tools/classIcons/GuiBitmapButtonCtrl.png | Bin 0 -> 756 bytes .../classIcons/GuiBitmapButtonTextCtrl.png | Bin 0 -> 726 bytes .../game/tools/classIcons/GuiBitmapCtrl.png | Bin 0 -> 520 bytes .../tools/classIcons/GuiBorderButtonCtrl.png | Bin 0 -> 144 bytes .../game/tools/classIcons/GuiButtonCtrl.png | Bin 0 -> 292 bytes .../game/tools/classIcons/GuiCheckBoxCtrl.png | Bin 0 -> 459 bytes .../tools/classIcons/GuiColorPickerCtrl.png | Bin 0 -> 396 bytes .../game/tools/classIcons/GuiContainer.png | Bin 0 -> 138 bytes .../game/tools/classIcons/GuiControl.png | Bin 0 -> 233 bytes .../classIcons/GuiControlArrayControl.png | Bin 0 -> 194 bytes .../game/tools/classIcons/GuiCrossHairHud.png | Bin 0 -> 439 bytes .../game/tools/classIcons/GuiDecoyCtrl.png | Bin 0 -> 178 bytes .../classIcons/GuiDragAndDropControl.png | Bin 0 -> 479 bytes .../classIcons/GuiDynamicCtrlArrayControl.png | Bin 0 -> 248 bytes .../tools/classIcons/GuiFadeinBitmapCtrl.png | Bin 0 -> 596 bytes .../game/tools/classIcons/GuiFileTreeCtrl.png | Bin 0 -> 196 bytes .../game/tools/classIcons/GuiFilterCtrl.png | Bin 0 -> 227 bytes .../game/tools/classIcons/GuiFormCtrl.png | Bin 0 -> 183 bytes .../game/tools/classIcons/GuiFrameSetCtrl.png | Bin 0 -> 233 bytes .../classIcons/GuiGradientSwatchCtrl.png | Bin 0 -> 176 bytes .../game/tools/classIcons/GuiGraphCtrl.png | Bin 0 -> 466 bytes .../game/tools/classIcons/GuiHealthBarHud.png | Bin 0 -> 153 bytes .../tools/classIcons/GuiIconButtonCtrl.png | Bin 0 -> 424 bytes .../game/tools/classIcons/GuiListBoxCtrl.png | Bin 0 -> 326 bytes .../game/tools/classIcons/GuiMLTextCtrl.png | Bin 0 -> 156 bytes .../tools/classIcons/GuiMLTextEditCtrl.png | Bin 0 -> 181 bytes .../game/tools/classIcons/GuiMenuBar.png | Bin 0 -> 171 bytes .../game/tools/classIcons/GuiObjectView.png | Bin 0 -> 407 bytes .../game/tools/classIcons/GuiPanel.png | Bin 0 -> 182 bytes .../tools/classIcons/GuiPopUpMenuCtrl.png | Bin 0 -> 230 bytes .../tools/classIcons/GuiPopUpMenuCtrlEx.png | Bin 0 -> 248 bytes .../classIcons/GuiProgressBitmapCtrl.png | Bin 0 -> 263 bytes .../game/tools/classIcons/GuiProgressCtrl.png | Bin 0 -> 185 bytes .../game/tools/classIcons/GuiRadioCtrl.png | Bin 0 -> 538 bytes .../game/tools/classIcons/GuiRectHandles.png | Bin 0 -> 204 bytes .../game/tools/classIcons/GuiRolloutCtrl.png | Bin 0 -> 225 bytes .../game/tools/classIcons/GuiScrollCtrl.png | Bin 0 -> 313 bytes .../tools/classIcons/GuiSplitContainer.png | Bin 0 -> 174 bytes .../game/tools/classIcons/GuiStackControl.png | Bin 0 -> 219 bytes .../tools/classIcons/GuiSwatchButtonCtrl.png | Bin 0 -> 176 bytes .../game/tools/classIcons/GuiTabBookCtrl.png | Bin 0 -> 466 bytes .../game/tools/classIcons/GuiTabPageCtrl.png | Bin 0 -> 331 bytes .../game/tools/classIcons/GuiTextCtrl.png | Bin 0 -> 206 bytes .../game/tools/classIcons/GuiTextEditCtrl.png | Bin 0 -> 247 bytes .../classIcons/GuiTextEditSliderCtrl.png | Bin 0 -> 289 bytes .../game/tools/classIcons/GuiTextListCtrl.png | Bin 0 -> 153 bytes .../game/tools/classIcons/GuiTheoraCtrl.png | Bin 0 -> 636 bytes .../game/tools/classIcons/GuiTreeViewCtrl.png | Bin 0 -> 196 bytes .../classIcons/GuiWindowCollapseCtrl.png | Bin 0 -> 235 bytes .../game/tools/classIcons/GuiWindowCtrl.png | Bin 0 -> 235 bytes .../BaseGame/game/tools/classIcons/Item.png | Bin 0 -> 766 bytes .../game/tools/classIcons/LevelInfo.png | Bin 0 -> 744 bytes .../game/tools/classIcons/Lightning.png | Bin 0 -> 470 bytes .../BaseGame/game/tools/classIcons/Marker.png | Bin 0 -> 169 bytes .../game/tools/classIcons/MeshRoad.png | Bin 0 -> 430 bytes .../game/tools/classIcons/MissionArea.png | Bin 0 -> 591 bytes .../game/tools/classIcons/NavMesh.png | Bin 0 -> 106 bytes .../game/tools/classIcons/NavPath.png | Bin 0 -> 241 bytes .../game/tools/classIcons/ParticleEmitter.png | Bin 0 -> 773 bytes .../tools/classIcons/ParticleEmitterNode.png | Bin 0 -> 426 bytes .../BaseGame/game/tools/classIcons/Path.png | Bin 0 -> 323 bytes .../game/tools/classIcons/PhysicalZone.png | Bin 0 -> 464 bytes .../BaseGame/game/tools/classIcons/Player.png | Bin 0 -> 480 bytes .../game/tools/classIcons/PointLight.png | Bin 0 -> 503 bytes .../BaseGame/game/tools/classIcons/Portal.png | Bin 0 -> 390 bytes .../game/tools/classIcons/Precipitation.png | Bin 0 -> 381 bytes .../BaseGame/game/tools/classIcons/Prefab.png | Bin 0 -> 688 bytes .../game/tools/classIcons/PxCloth.png | Bin 0 -> 573 bytes .../BaseGame/game/tools/classIcons/River.png | Bin 0 -> 789 bytes .../game/tools/classIcons/SFXEmitter.png | Bin 0 -> 368 bytes .../game/tools/classIcons/ScatterSky.png | Bin 0 -> 624 bytes .../game/tools/classIcons/SceneObject.png | Bin 0 -> 369 bytes .../game/tools/classIcons/SimDataBlock.png | Bin 0 -> 575 bytes .../game/tools/classIcons/SimObject.png | Bin 0 -> 358 bytes .../BaseGame/game/tools/classIcons/SimSet.png | Bin 0 -> 236 bytes .../BaseGame/game/tools/classIcons/SkyBox.png | Bin 0 -> 749 bytes .../game/tools/classIcons/SpawnSphere.png | Bin 0 -> 748 bytes .../game/tools/classIcons/SpotLight.png | Bin 0 -> 732 bytes .../BaseGame/game/tools/classIcons/Sun.png | Bin 0 -> 419 bytes .../tools/classIcons/TSForestItemData.png | Bin 0 -> 436 bytes .../game/tools/classIcons/TSStatic.png | Bin 0 -> 767 bytes .../game/tools/classIcons/TerrainBlock.png | Bin 0 -> 607 bytes .../game/tools/classIcons/TimeOfDay.png | Bin 0 -> 744 bytes .../game/tools/classIcons/Trigger.png | Bin 0 -> 513 bytes .../game/tools/classIcons/VolumetricFog.png | Bin 0 -> 3642 bytes .../game/tools/classIcons/WaterBlock.png | Bin 0 -> 543 bytes .../game/tools/classIcons/WaterPlane.png | Bin 0 -> 787 bytes .../BaseGame/game/tools/classIcons/Zone.png | Bin 0 -> 406 bytes .../game/tools/classIcons/cameraSpawn.png | Bin 0 -> 778 bytes .../BaseGame/game/tools/classIcons/decal.png | Bin 0 -> 567 bytes .../game/tools/classIcons/decalNode.png | Bin 0 -> 693 bytes .../game/tools/classIcons/default.png | Bin 0 -> 356 bytes .../tools/classIcons/fxFoliageReplicator.png | Bin 0 -> 538 bytes .../tools/classIcons/fxShapeReplicator.png | Bin 0 -> 560 bytes .../tools/classIcons/interiorInstance.png | Bin 0 -> 418 bytes .../classIcons/particleEffecterObject.png | Bin 0 -> 727 bytes .../classIcons/particleEmitterObject.png | Bin 0 -> 773 bytes .../tools/classIcons/particleSimulation.png | Bin 0 -> 766 bytes .../game/tools/classIcons/pathMarker.png | Bin 0 -> 169 bytes .../game/tools/classIcons/volumeLight.png | Bin 0 -> 665 bytes .../gui/superToolTipDlg.ed.gui | 45 + .../game/tools/componentEditor/main.cs | 28 + .../scripts/componentEditor.ed.cs | 233 + .../scripts/superToolTipDlg.ed.cs | 155 + .../game/tools/convexEditor/convexEditor.cs | 53 + .../tools/convexEditor/convexEditorGui.cs | 62 + .../tools/convexEditor/convexEditorGui.gui | 440 ++ .../convexEditorSettingsTab.ed.gui | 180 + .../convexEditor/convexEditorToolbar.ed.gui | 121 + .../tools/convexEditor/images/512_orange.png | Bin 0 -> 6295 bytes .../images/convex-editor-btn_d.png | Bin 0 -> 871 bytes .../images/convex-editor-btn_h.png | Bin 0 -> 1059 bytes .../images/convex-editor-btn_n.png | Bin 0 -> 603 bytes .../tools/convexEditor/images/materials.cs | 5 + .../convexEditor/images/split-face-btn_d.png | Bin 0 -> 743 bytes .../convexEditor/images/split-face-btn_h.png | Bin 0 -> 915 bytes .../convexEditor/images/split-face-btn_i.png | Bin 0 -> 425 bytes .../convexEditor/images/split-face-btn_n.png | Bin 0 -> 543 bytes .../BaseGame/game/tools/convexEditor/main.cs | 220 + .../DatablockEditorCreatePrompt.ed.gui | 224 + .../DatablockEditorInspectorWindow.ed.gui | 211 + .../DatablockEditorTreeWindow.ed.gui | 311 + .../tools/datablockEditor/datablockEditor.cs | 885 +++ .../datablockEditor/datablockEditorUndo.cs | 159 + .../game/tools/datablockEditor/main.cs | 66 + .../debugger/gui/breakConditionDlg.ed.gui | 145 + .../game/tools/debugger/gui/connectDlg.ed.gui | 148 + .../game/tools/debugger/gui/debugger.ed.gui | 583 ++ .../tools/debugger/gui/editWatchDlg.ed.gui | 93 + .../game/tools/debugger/gui/findDlg.ed.gui | 93 + .../game/tools/debugger/gui/watchDlg.ed.gui | 92 + .../BaseGame/game/tools/debugger/main.cs | 68 + .../tools/debugger/scripts/debugger.ed.cs | 508 ++ .../game/tools/decalEditor/add-decal_d.png | Bin 0 -> 880 bytes .../game/tools/decalEditor/add-decal_h.png | Bin 0 -> 1063 bytes .../game/tools/decalEditor/add-decal_n.png | Bin 0 -> 726 bytes .../game/tools/decalEditor/decal-editor_d.png | Bin 0 -> 937 bytes .../game/tools/decalEditor/decal-editor_h.png | Bin 0 -> 1132 bytes .../game/tools/decalEditor/decal-editor_n.png | Bin 0 -> 750 bytes .../game/tools/decalEditor/decalEditor.cs | 33 + .../tools/decalEditor/decalEditorActions.cs | 122 + .../game/tools/decalEditor/decalEditorGui.cs | 343 ++ .../game/tools/decalEditor/decalEditorGui.gui | 831 +++ .../BaseGame/game/tools/decalEditor/main.cs | 199 + .../tools/editorClasses/gui/images/button.png | Bin 0 -> 1545 bytes .../editorClasses/gui/images/button_left.png | Bin 0 -> 1450 bytes .../gui/images/button_middle.png | Bin 0 -> 1324 bytes .../editorClasses/gui/images/button_right.png | Bin 0 -> 1463 bytes .../gui/images/button_toolbar.png | Bin 0 -> 1219 bytes .../editorClasses/gui/images/dropDown.png | Bin 0 -> 6848 bytes .../tools/editorClasses/gui/images/form.png | Bin 0 -> 3183 bytes .../editorClasses/gui/images/formMenu.png | Bin 0 -> 1396 bytes .../editorClasses/gui/images/iconAccept.png | Bin 0 -> 917 bytes .../editorClasses/gui/images/iconCancel.png | Bin 0 -> 853 bytes .../gui/images/iconInformation.png | Bin 0 -> 918 bytes .../editorClasses/gui/images/iconNext.png | Bin 0 -> 783 bytes .../editorClasses/gui/images/iconPrevious.png | Bin 0 -> 783 bytes .../editorClasses/gui/images/iconRSSNews.png | Bin 0 -> 583 bytes .../editorClasses/gui/images/iconSave.png | Bin 0 -> 752 bytes .../editorClasses/gui/images/panel_button.png | Bin 0 -> 1155 bytes .../editorClasses/gui/images/panel_dark.png | Bin 0 -> 502 bytes .../editorClasses/gui/images/panel_light.png | Bin 0 -> 481 bytes .../editorClasses/gui/images/panel_medium.png | Bin 0 -> 510 bytes .../editorClasses/gui/images/rollout.png | Bin 0 -> 625 bytes .../editorClasses/gui/images/rollout_dark.png | Bin 0 -> 953 bytes .../gui/images/rollout_plusminus_header.png | Bin 0 -> 2955 bytes .../images/rollout_plusminus_transparent.png | Bin 0 -> 865 bytes .../editorClasses/gui/images/rollout_thin.png | Bin 0 -> 3133 bytes .../gui/images/rollout_thin_light.png | Bin 0 -> 3079 bytes .../tools/editorClasses/gui/images/scroll.png | Bin 0 -> 5655 bytes .../tools/editorClasses/gui/images/slider.png | Bin 0 -> 825 bytes .../gui/images/start/background.jpg | Bin 0 -> 101809 bytes .../editorClasses/gui/images/start/create.png | Bin 0 -> 35059 bytes .../gui/images/start/create_d.png | Bin 0 -> 79750 bytes .../gui/images/start/create_h.png | Bin 0 -> 67234 bytes .../gui/images/start/create_i.png | Bin 0 -> 25395 bytes .../editorClasses/gui/images/start/import.png | Bin 0 -> 36988 bytes .../gui/images/start/import_d.png | Bin 0 -> 52356 bytes .../gui/images/start/import_h.png | Bin 0 -> 51126 bytes .../gui/images/start/import_i.png | Bin 0 -> 16087 bytes .../gui/images/start/navPanel.png | Bin 0 -> 10400 bytes .../editorClasses/gui/images/start/open.png | Bin 0 -> 59222 bytes .../editorClasses/gui/images/start/open_d.png | Bin 0 -> 106165 bytes .../editorClasses/gui/images/start/open_h.png | Bin 0 -> 100137 bytes .../editorClasses/gui/images/start/open_i.png | Bin 0 -> 48906 bytes .../editorClasses/gui/images/start/splash.png | Bin 0 -> 40979 bytes .../gui/images/start/topBarLeft.png | Bin 0 -> 16251 bytes .../gui/images/start/topBarMiddle.png | Bin 0 -> 552 bytes .../gui/images/start/topBarRight.png | Bin 0 -> 7956 bytes .../editorClasses/gui/images/tabBook.png | Bin 0 -> 1128 bytes .../editorClasses/gui/images/textEdit.png | Bin 0 -> 2870 bytes .../editorClasses/gui/images/toolWindow.png | Bin 0 -> 9227 bytes .../editorClasses/gui/images/toolbar.png | Bin 0 -> 223 bytes .../editorClasses/gui/images/treeView.png | Bin 0 -> 4946 bytes .../tools/editorClasses/gui/images/window.png | Bin 0 -> 9112 bytes .../gui/panels/editor-menubar.png | Bin 0 -> 299 bytes .../gui/panels/icon-dropdownbar.png | Bin 0 -> 230 bytes .../panels/inspector-style-rollout-dark.png | Bin 0 -> 322 bytes .../panels/inspector-style-rollout-list.png | Bin 0 -> 252 bytes .../inspector-style-rollout-noheader.png | Bin 0 -> 950 bytes .../gui/panels/inspector-style-rollout.png | Bin 0 -> 302 bytes .../panels/inspector-style-rollout_inner.png | Bin 0 -> 519 bytes .../gui/panels/menu-fullborder.png | Bin 0 -> 339 bytes .../editorClasses/gui/panels/menubar.png | Bin 0 -> 238 bytes .../editorClasses/gui/panels/navPanel.png | Bin 0 -> 1199 bytes .../gui/panels/navPanelProfiles.ed.cs | 111 + .../gui/panels/navPanel_blue.png | Bin 0 -> 1224 bytes .../gui/panels/navPanel_green.png | Bin 0 -> 1236 bytes .../editorClasses/gui/panels/navPanel_red.png | Bin 0 -> 1230 bytes .../gui/panels/navPanel_white.png | Bin 0 -> 1177 bytes .../gui/panels/navPanel_yellow.png | Bin 0 -> 1215 bytes .../BaseGame/game/tools/editorClasses/main.cs | 86 + .../scripts/RSSNews/RSSFeedScript.ed.cs | 155 + .../scripts/RSSNews/RSSStructs.ed.cs | 153 + .../editorClasses/scripts/contextPopup.ed.cs | 210 + .../scripts/core/zip/zipFile.ed.cs | 38 + .../editorClasses/scripts/expandos.ed.cs | 37 + .../editorClasses/scripts/fileLoader.ed.cs | 108 + .../scripts/guiClasses/guiThumbnail.ed.cs | 76 + .../guiClasses/guiThumbnailPopup.ed.cs | 224 + .../editorClasses/scripts/guiFormClass.ed.cs | 616 ++ .../scripts/guiFormContentManager.ed.cs | 190 + .../scripts/guiFormLayoutManager.ed.cs | 394 ++ .../scripts/guiFormLibraryManager.ed.cs | 161 + .../scripts/guiFormMessageManager.ed.cs | 126 + .../scripts/guiFormReferenceManager.ed.cs | 119 + .../scripts/input/applicationEvents.ed.cs | 46 + .../scripts/input/dragDropEvents.ed.cs | 45 + .../scripts/input/inputEvents.ed.cs | 32 + .../editorClasses/scripts/platform/.gitignore | 1 + .../scripts/preferencesManager.ed.cs | 32 + .../scripts/projects/projectEvents.ed.cs | 115 + .../projects/projectInternalInterface.ed.cs | 188 + .../tools/editorClasses/scripts/utility.ed.cs | 47 + .../game/tools/forestEditor/brushes.cs | 22 + .../forestEditor/forestEditToolbar.ed.gui | 437 ++ .../game/tools/forestEditor/forestEditor.cs | 27 + .../tools/forestEditor/forestEditorGui.cs | 502 ++ .../tools/forestEditor/forestEditorGui.gui | 511 ++ .../forestEditor/images/erase-all-btn_d.png | Bin 0 -> 751 bytes .../forestEditor/images/erase-all-btn_h.png | Bin 0 -> 1012 bytes .../forestEditor/images/erase-all-btn_n.png | Bin 0 -> 671 bytes .../images/erase-element-btn_d.png | Bin 0 -> 653 bytes .../images/erase-element-btn_h.png | Bin 0 -> 945 bytes .../images/erase-element-btn_n.png | Bin 0 -> 619 bytes .../images/forest-editor-btn_d.png | Bin 0 -> 908 bytes .../images/forest-editor-btn_h.png | Bin 0 -> 1082 bytes .../images/forest-editor-btn_n.png | Bin 0 -> 677 bytes .../tools/forestEditor/images/new-brush_d.png | Bin 0 -> 694 bytes .../tools/forestEditor/images/new-brush_h.png | Bin 0 -> 693 bytes .../tools/forestEditor/images/new-brush_n.png | Bin 0 -> 636 bytes .../forestEditor/images/new-element_d.png | Bin 0 -> 437 bytes .../forestEditor/images/new-element_h.png | Bin 0 -> 440 bytes .../forestEditor/images/new-element_n.png | Bin 0 -> 412 bytes .../tools/forestEditor/images/new-mesh_d.png | Bin 0 -> 709 bytes .../tools/forestEditor/images/new-mesh_h.png | Bin 0 -> 722 bytes .../tools/forestEditor/images/new-mesh_n.png | Bin 0 -> 633 bytes .../images/paint-forest-btn_d.png | Bin 0 -> 940 bytes .../images/paint-forest-btn_h.png | Bin 0 -> 1127 bytes .../images/paint-forest-btn_n.png | Bin 0 -> 754 bytes .../BaseGame/game/tools/forestEditor/main.cs | 311 + .../BaseGame/game/tools/forestEditor/tools.cs | 61 + .../game/tools/gui/EditorLoadingGui.gui | 72 + .../game/tools/gui/GuiEaseEditDlg.ed.cs | 183 + .../game/tools/gui/GuiEaseEditDlg.ed.gui | 332 ++ .../game/tools/gui/colladaImport.ed.gui | 1698 ++++++ .../game/tools/gui/colorPicker.ed.gui | 1149 ++++ .../BaseGame/game/tools/gui/cursors.ed.cs | 63 + .../game/tools/gui/fileDialogBase.ed.cs | 331 ++ .../BaseGame/game/tools/gui/guiDialogs.ed.cs | 38 + .../game/tools/gui/guiObjectInspector.ed.cs | 248 + .../game/tools/gui/guiObjectInspector.ed.gui | 401 ++ .../tools/gui/guiPlatformGenericMenubar.ed.cs | 19 + .../gui/guiPlatformGenericMenubar.ed.gui | 14 + .../images/ColladaImport/iconAnimation.png | Bin 0 -> 480 bytes .../ColladaImport/iconExistingMaterial.png | Bin 0 -> 918 bytes .../images/ColladaImport/iconIgnoreNode.png | Bin 0 -> 853 bytes .../gui/images/ColladaImport/iconLight.png | Bin 0 -> 503 bytes .../gui/images/ColladaImport/iconMaterial.png | Bin 0 -> 256 bytes .../gui/images/ColladaImport/iconMesh.png | Bin 0 -> 767 bytes .../gui/images/ColladaImport/iconNode.png | Bin 0 -> 917 bytes .../gui/images/GUI-editor/align-bottom_d.png | Bin 0 -> 375 bytes .../gui/images/GUI-editor/align-bottom_h.png | Bin 0 -> 376 bytes .../gui/images/GUI-editor/align-bottom_n.png | Bin 0 -> 348 bytes .../gui/images/GUI-editor/align-left_d.png | Bin 0 -> 384 bytes .../gui/images/GUI-editor/align-left_h.png | Bin 0 -> 382 bytes .../gui/images/GUI-editor/align-left_n.png | Bin 0 -> 368 bytes .../gui/images/GUI-editor/align-right_d.png | Bin 0 -> 384 bytes .../gui/images/GUI-editor/align-right_h.png | Bin 0 -> 383 bytes .../gui/images/GUI-editor/align-right_n.png | Bin 0 -> 363 bytes .../gui/images/GUI-editor/align-top_d.png | Bin 0 -> 385 bytes .../gui/images/GUI-editor/align-top_h.png | Bin 0 -> 383 bytes .../gui/images/GUI-editor/align-top_n.png | Bin 0 -> 356 bytes .../images/GUI-editor/bring-to-front_d.png | Bin 0 -> 322 bytes .../images/GUI-editor/bring-to-front_h.png | Bin 0 -> 323 bytes .../images/GUI-editor/bring-to-front_n.png | Bin 0 -> 325 bytes .../gui/images/GUI-editor/centersnap_d.png | Bin 0 -> 621 bytes .../gui/images/GUI-editor/centersnap_h.png | Bin 0 -> 821 bytes .../gui/images/GUI-editor/centersnap_n.png | Bin 0 -> 443 bytes .../GUI-editor/distribute-horizontal_d.png | Bin 0 -> 470 bytes .../GUI-editor/distribute-horizontal_h.png | Bin 0 -> 469 bytes .../GUI-editor/distribute-horizontal_n.png | Bin 0 -> 474 bytes .../GUI-editor/distribute-vertical_d.png | Bin 0 -> 476 bytes .../GUI-editor/distribute-vertical_h.png | Bin 0 -> 475 bytes .../GUI-editor/distribute-vertical_n.png | Bin 0 -> 462 bytes .../gui/images/GUI-editor/edgesnap_d.png | Bin 0 -> 552 bytes .../gui/images/GUI-editor/edgesnap_h.png | Bin 0 -> 773 bytes .../gui/images/GUI-editor/edgesnap_n.png | Bin 0 -> 361 bytes .../gui/images/GUI-editor/gui-library_d.png | Bin 0 -> 864 bytes .../gui/images/GUI-editor/gui-library_h.png | Bin 0 -> 1074 bytes .../gui/images/GUI-editor/gui-library_n.png | Bin 0 -> 665 bytes .../images/GUI-editor/horizontal-center_d.png | Bin 0 -> 385 bytes .../images/GUI-editor/horizontal-center_h.png | Bin 0 -> 385 bytes .../images/GUI-editor/horizontal-center_n.png | Bin 0 -> 374 bytes .../gui/images/GUI-editor/send-to-back_d.png | Bin 0 -> 325 bytes .../gui/images/GUI-editor/send-to-back_h.png | Bin 0 -> 326 bytes .../gui/images/GUI-editor/send-to-back_n.png | Bin 0 -> 328 bytes .../gui/images/GUI-editor/snap-grid_d.png | Bin 0 -> 653 bytes .../gui/images/GUI-editor/snap-grid_h.png | Bin 0 -> 904 bytes .../gui/images/GUI-editor/snap-grid_n.png | Bin 0 -> 683 bytes .../images/GUI-editor/vertical-center_d.png | Bin 0 -> 393 bytes .../images/GUI-editor/vertical-center_h.png | Bin 0 -> 391 bytes .../images/GUI-editor/vertical-center_n.png | Bin 0 -> 373 bytes .../BaseGame/game/tools/gui/images/NESW.png | Bin 0 -> 3092 bytes .../BaseGame/game/tools/gui/images/NWSE.png | Bin 0 -> 3101 bytes .../gui/images/add-simgroup-btn_ctrl_d.png | Bin 0 -> 242 bytes .../gui/images/add-simgroup-btn_ctrl_h.png | Bin 0 -> 246 bytes .../gui/images/add-simgroup-btn_ctrl_n.png | Bin 0 -> 207 bytes .../tools/gui/images/add-simgroup-btn_d.png | Bin 0 -> 242 bytes .../tools/gui/images/add-simgroup-btn_h.png | Bin 0 -> 246 bytes .../tools/gui/images/add-simgroup-btn_n.png | Bin 0 -> 240 bytes .../game/tools/gui/images/arrowbtn_d.png | Bin 0 -> 204 bytes .../game/tools/gui/images/arrowbtn_n.png | Bin 0 -> 203 bytes .../game/tools/gui/images/axis-icon_-x.png | Bin 0 -> 178 bytes .../game/tools/gui/images/axis-icon_-y.png | Bin 0 -> 224 bytes .../game/tools/gui/images/axis-icon_-z.png | Bin 0 -> 202 bytes .../game/tools/gui/images/axis-icon_x.png | Bin 0 -> 168 bytes .../game/tools/gui/images/axis-icon_y.png | Bin 0 -> 222 bytes .../game/tools/gui/images/axis-icon_z.png | Bin 0 -> 191 bytes .../BaseGame/game/tools/gui/images/button.png | Bin 0 -> 1153 bytes .../game/tools/gui/images/camera-btn_d.png | Bin 0 -> 547 bytes .../game/tools/gui/images/camera-btn_h.png | Bin 0 -> 551 bytes .../game/tools/gui/images/camera-btn_n.png | Bin 0 -> 497 bytes .../game/tools/gui/images/checkbox-list.png | Bin 0 -> 857 bytes .../tools/gui/images/checkbox-list_fliped.png | Bin 0 -> 960 bytes .../tools/gui/images/checkbox-menubar.png | Bin 0 -> 1027 bytes .../game/tools/gui/images/checkbox.png | Bin 0 -> 3943 bytes .../game/tools/gui/images/clear-btn_d.png | Bin 0 -> 593 bytes .../game/tools/gui/images/clear-btn_h.png | Bin 0 -> 595 bytes .../game/tools/gui/images/clear-btn_n.png | Bin 0 -> 377 bytes .../game/tools/gui/images/clear-icon_d.png | Bin 0 -> 621 bytes .../game/tools/gui/images/clear-icon_h.png | Bin 0 -> 670 bytes .../game/tools/gui/images/clear-icon_n.png | Bin 0 -> 651 bytes .../tools/gui/images/collapse-toolbar_d.png | Bin 0 -> 280 bytes .../tools/gui/images/collapse-toolbar_h.png | Bin 0 -> 468 bytes .../tools/gui/images/collapse-toolbar_n.png | Bin 0 -> 439 bytes .../game/tools/gui/images/copy-btn_d.png | Bin 0 -> 542 bytes .../game/tools/gui/images/copy-btn_h.png | Bin 0 -> 536 bytes .../game/tools/gui/images/copy-btn_i.png | Bin 0 -> 490 bytes .../game/tools/gui/images/copy-btn_n.png | Bin 0 -> 496 bytes .../game/tools/gui/images/crosshair.png | Bin 0 -> 144 bytes .../game/tools/gui/images/crosshair_blue.png | Bin 0 -> 134 bytes .../game/tools/gui/images/delete_d.png | Bin 0 -> 622 bytes .../game/tools/gui/images/delete_h.png | Bin 0 -> 638 bytes .../game/tools/gui/images/delete_n.png | Bin 0 -> 633 bytes .../game/tools/gui/images/dropDown-tab.png | Bin 0 -> 1062 bytes .../game/tools/gui/images/dropDown.png | Bin 0 -> 1309 bytes .../gui/images/dropdown-button-arrow.png | Bin 0 -> 132 bytes .../tools/gui/images/dropdown-textEdit.png | Bin 0 -> 390 bytes .../game/tools/gui/images/dropslider_d.png | Bin 0 -> 433 bytes .../game/tools/gui/images/dropslider_h.png | Bin 0 -> 431 bytes .../game/tools/gui/images/dropslider_n.png | Bin 0 -> 428 bytes .../tools/gui/images/expand-toolbar_d.png | Bin 0 -> 309 bytes .../tools/gui/images/expand-toolbar_h.png | Bin 0 -> 477 bytes .../tools/gui/images/expand-toolbar_n.png | Bin 0 -> 452 bytes .../BaseGame/game/tools/gui/images/folder.png | Bin 0 -> 236 bytes .../game/tools/gui/images/folderUp.png | Bin 0 -> 670 bytes .../game/tools/gui/images/folderUp_d.png | Bin 0 -> 696 bytes .../game/tools/gui/images/folderUp_h.png | Bin 0 -> 696 bytes .../game/tools/gui/images/group-border.png | Bin 0 -> 1273 bytes .../game/tools/gui/images/iconAccept.png | Bin 0 -> 917 bytes .../game/tools/gui/images/iconAdd.png | Bin 0 -> 847 bytes .../game/tools/gui/images/iconCancel.png | Bin 0 -> 853 bytes .../game/tools/gui/images/iconCollada.png | Bin 0 -> 477 bytes .../game/tools/gui/images/iconDelete.png | Bin 0 -> 846 bytes .../game/tools/gui/images/iconIcon.png | Bin 0 -> 256 bytes .../game/tools/gui/images/iconInformation.png | Bin 0 -> 918 bytes .../game/tools/gui/images/iconList.png | Bin 0 -> 222 bytes .../game/tools/gui/images/iconLocked.png | Bin 0 -> 813 bytes .../game/tools/gui/images/iconNew.png | Bin 0 -> 611 bytes .../game/tools/gui/images/iconOpen.png | Bin 0 -> 873 bytes .../game/tools/gui/images/iconRefresh.png | Bin 0 -> 789 bytes .../game/tools/gui/images/iconSave.png | Bin 0 -> 752 bytes .../game/tools/gui/images/iconUnlocked.png | Bin 0 -> 727 bytes .../game/tools/gui/images/iconVisible.png | Bin 0 -> 735 bytes .../game/tools/gui/images/iconbutton.png | Bin 0 -> 1013 bytes .../game/tools/gui/images/iconbuttonsmall.png | Bin 0 -> 1014 bytes .../tools/gui/images/inactive-overlay.png | Bin 0 -> 131 bytes .../game/tools/gui/images/layers-btn_d.png | Bin 0 -> 523 bytes .../game/tools/gui/images/layers-btn_h.png | Bin 0 -> 523 bytes .../game/tools/gui/images/layers-btn_n.png | Bin 0 -> 368 bytes .../game/tools/gui/images/leftRight.png | Bin 0 -> 3098 bytes .../BaseGame/game/tools/gui/images/lock_d.png | Bin 0 -> 405 bytes .../BaseGame/game/tools/gui/images/lock_h.png | Bin 0 -> 445 bytes .../BaseGame/game/tools/gui/images/lock_n.png | Bin 0 -> 433 bytes .../game/tools/gui/images/menubar/arrow_d.png | Bin 0 -> 560 bytes .../game/tools/gui/images/menubar/arrow_h.png | Bin 0 -> 699 bytes .../game/tools/gui/images/menubar/arrow_n.png | Bin 0 -> 489 bytes .../gui/images/menubar/bounds-center_d.png | Bin 0 -> 1062 bytes .../gui/images/menubar/bounds-center_h.png | Bin 0 -> 1339 bytes .../gui/images/menubar/bounds-center_n.png | Bin 0 -> 889 bytes .../tools/gui/images/menubar/delete-btn_d.png | Bin 0 -> 934 bytes .../tools/gui/images/menubar/delete-btn_h.png | Bin 0 -> 1101 bytes .../tools/gui/images/menubar/delete-btn_i.png | Bin 0 -> 708 bytes .../tools/gui/images/menubar/delete-btn_n.png | Bin 0 -> 617 bytes .../gui/images/menubar/explode-prefab_d.png | Bin 0 -> 1559 bytes .../gui/images/menubar/explode-prefab_h.png | Bin 0 -> 1752 bytes .../gui/images/menubar/explode-prefab_i.png | Bin 0 -> 1195 bytes .../gui/images/menubar/explode-prefab_n.png | Bin 0 -> 1300 bytes .../gui/images/menubar/fit-selection_d.png | Bin 0 -> 977 bytes .../gui/images/menubar/fit-selection_h.png | Bin 0 -> 1253 bytes .../gui/images/menubar/fit-selection_n.png | Bin 0 -> 694 bytes .../gui/images/menubar/object-center_d.png | Bin 0 -> 1119 bytes .../gui/images/menubar/object-center_h.png | Bin 0 -> 1288 bytes .../gui/images/menubar/object-center_n.png | Bin 0 -> 936 bytes .../gui/images/menubar/object-node-icon_d.png | Bin 0 -> 669 bytes .../gui/images/menubar/object-node-icon_h.png | Bin 0 -> 889 bytes .../gui/images/menubar/object-node-icon_n.png | Bin 0 -> 746 bytes .../images/menubar/object-node-lable_d.png | Bin 0 -> 571 bytes .../images/menubar/object-node-lable_h.png | Bin 0 -> 816 bytes .../images/menubar/object-node-lable_n.png | Bin 0 -> 628 bytes .../gui/images/menubar/object-transform_d.png | Bin 0 -> 1257 bytes .../gui/images/menubar/object-transform_h.png | Bin 0 -> 1491 bytes .../gui/images/menubar/object-transform_n.png | Bin 0 -> 1015 bytes .../tools/gui/images/menubar/orbit-cam_d.png | Bin 0 -> 1199 bytes .../tools/gui/images/menubar/orbit-cam_h.png | Bin 0 -> 1414 bytes .../tools/gui/images/menubar/orbit-cam_n.png | Bin 0 -> 970 bytes .../tools/gui/images/menubar/rotate_d.png | Bin 0 -> 844 bytes .../tools/gui/images/menubar/rotate_h.png | Bin 0 -> 1046 bytes .../tools/gui/images/menubar/rotate_n.png | Bin 0 -> 816 bytes .../game/tools/gui/images/menubar/scale_d.png | Bin 0 -> 832 bytes .../game/tools/gui/images/menubar/scale_h.png | Bin 0 -> 1062 bytes .../game/tools/gui/images/menubar/scale_n.png | Bin 0 -> 821 bytes .../gui/images/menubar/select-bounds_d.png | Bin 0 -> 1026 bytes .../gui/images/menubar/select-bounds_h.png | Bin 0 -> 1263 bytes .../gui/images/menubar/select-bounds_n.png | Bin 0 -> 950 bytes .../images/menubar/selection-to-prefab_d.png | Bin 0 -> 1215 bytes .../images/menubar/selection-to-prefab_h.png | Bin 0 -> 1400 bytes .../images/menubar/selection-to-prefab_i.png | Bin 0 -> 916 bytes .../images/menubar/selection-to-prefab_n.png | Bin 0 -> 974 bytes .../tools/gui/images/menubar/show-grid_d.png | Bin 0 -> 387 bytes .../tools/gui/images/menubar/show-grid_h.png | Bin 0 -> 592 bytes .../tools/gui/images/menubar/show-grid_n.png | Bin 0 -> 310 bytes .../gui/images/menubar/show-preview_d.png | Bin 0 -> 1154 bytes .../gui/images/menubar/show-preview_h.png | Bin 0 -> 1381 bytes .../gui/images/menubar/show-preview_n.png | Bin 0 -> 967 bytes .../gui/images/menubar/smooth-cam-rot_d.png | Bin 0 -> 1358 bytes .../gui/images/menubar/smooth-cam-rot_h.png | Bin 0 -> 1597 bytes .../gui/images/menubar/smooth-cam-rot_n.png | Bin 0 -> 1616 bytes .../tools/gui/images/menubar/smooth-cam_d.png | Bin 0 -> 1178 bytes .../tools/gui/images/menubar/smooth-cam_h.png | Bin 0 -> 1407 bytes .../tools/gui/images/menubar/smooth-cam_n.png | Bin 0 -> 1611 bytes .../gui/images/menubar/snap-bounds_d.png | Bin 0 -> 950 bytes .../gui/images/menubar/snap-bounds_h.png | Bin 0 -> 1249 bytes .../gui/images/menubar/snap-bounds_n.png | Bin 0 -> 962 bytes .../tools/gui/images/menubar/snap-grid_d.png | Bin 0 -> 653 bytes .../tools/gui/images/menubar/snap-grid_h.png | Bin 0 -> 904 bytes .../tools/gui/images/menubar/snap-grid_n.png | Bin 0 -> 683 bytes .../gui/images/menubar/snap-objects_d.png | Bin 0 -> 930 bytes .../gui/images/menubar/snap-objects_h.png | Bin 0 -> 1140 bytes .../gui/images/menubar/snap-objects_n.png | Bin 0 -> 666 bytes .../gui/images/menubar/snap-terrain_d.png | Bin 0 -> 963 bytes .../gui/images/menubar/snap-terrain_h.png | Bin 0 -> 1178 bytes .../gui/images/menubar/snap-terrain_n.png | Bin 0 -> 967 bytes .../images/menubar/snapping-settings_d.png | Bin 0 -> 945 bytes .../images/menubar/snapping-settings_h.png | Bin 0 -> 1203 bytes .../images/menubar/snapping-settings_n.png | Bin 0 -> 714 bytes .../tools/gui/images/menubar/translate_d.png | Bin 0 -> 991 bytes .../tools/gui/images/menubar/translate_h.png | Bin 0 -> 1175 bytes .../tools/gui/images/menubar/translate_n.png | Bin 0 -> 1013 bytes .../images/menubar/visibility-toggle_d.png | Bin 0 -> 1040 bytes .../images/menubar/visibility-toggle_h.png | Bin 0 -> 1195 bytes .../images/menubar/visibility-toggle_n.png | Bin 0 -> 790 bytes .../gui/images/menubar/world-transform_d.png | Bin 0 -> 1203 bytes .../gui/images/menubar/world-transform_h.png | Bin 0 -> 1435 bytes .../gui/images/menubar/world-transform_n.png | Bin 0 -> 934 bytes .../BaseGame/game/tools/gui/images/move.png | Bin 0 -> 3232 bytes .../tools/gui/images/new-folder-btn_d.png | Bin 0 -> 242 bytes .../tools/gui/images/new-folder-btn_h.png | Bin 0 -> 246 bytes .../tools/gui/images/new-folder-btn_n.png | Bin 0 -> 240 bytes .../BaseGame/game/tools/gui/images/new_d.png | Bin 0 -> 200 bytes .../BaseGame/game/tools/gui/images/new_h.png | Bin 0 -> 200 bytes .../BaseGame/game/tools/gui/images/new_n.png | Bin 0 -> 200 bytes .../game/tools/gui/images/open-file_d.png | Bin 0 -> 481 bytes .../game/tools/gui/images/open-file_h.png | Bin 0 -> 451 bytes .../game/tools/gui/images/open-file_n.png | Bin 0 -> 488 bytes .../game/tools/gui/images/radioButton.png | Bin 0 -> 843 bytes .../game/tools/gui/images/reset-icon_d.png | Bin 0 -> 418 bytes .../game/tools/gui/images/reset-icon_h.png | Bin 0 -> 434 bytes .../game/tools/gui/images/reset-icon_n.png | Bin 0 -> 431 bytes .../game/tools/gui/images/retarget-btn_d.png | Bin 0 -> 594 bytes .../game/tools/gui/images/retarget-btn_h.png | Bin 0 -> 590 bytes .../game/tools/gui/images/retarget-btn_i.png | Bin 0 -> 499 bytes .../game/tools/gui/images/retarget-btn_n.png | Bin 0 -> 498 bytes .../game/tools/gui/images/rl-loadingbar.png | Bin 0 -> 630 bytes .../game/tools/gui/images/save-all_d.png | Bin 0 -> 390 bytes .../game/tools/gui/images/save-all_h.png | Bin 0 -> 360 bytes .../game/tools/gui/images/save-all_i.png | Bin 0 -> 372 bytes .../game/tools/gui/images/save-all_n.png | Bin 0 -> 395 bytes .../game/tools/gui/images/save-as_d.png | Bin 0 -> 508 bytes .../game/tools/gui/images/save-as_h.png | Bin 0 -> 477 bytes .../game/tools/gui/images/save-as_i.png | Bin 0 -> 456 bytes .../game/tools/gui/images/save-as_n.png | Bin 0 -> 512 bytes .../game/tools/gui/images/save-icon_d.png | Bin 0 -> 335 bytes .../game/tools/gui/images/save-icon_h.png | Bin 0 -> 299 bytes .../game/tools/gui/images/save-icon_i.png | Bin 0 -> 299 bytes .../game/tools/gui/images/save-icon_n.png | Bin 0 -> 338 bytes .../game/tools/gui/images/scrollBar.png | Bin 0 -> 3332 bytes .../game/tools/gui/images/separator-h.png | Bin 0 -> 117 bytes .../game/tools/gui/images/separator-v.png | Bin 0 -> 118 bytes .../game/tools/gui/images/slider-w-box.png | Bin 0 -> 982 bytes .../BaseGame/game/tools/gui/images/slider.png | Bin 0 -> 908 bytes .../game/tools/gui/images/tab-border.png | Bin 0 -> 1203 bytes .../BaseGame/game/tools/gui/images/tab.png | Bin 0 -> 939 bytes .../game/tools/gui/images/textEdit.png | Bin 0 -> 2989 bytes .../game/tools/gui/images/textEditFrame.png | Bin 0 -> 250 bytes .../tools/gui/images/textEditSliderBox.png | Bin 0 -> 226 bytes .../gui/images/thumbHightlightButton.png | Bin 0 -> 778 bytes .../game/tools/gui/images/toolbar-window.png | Bin 0 -> 1938 bytes .../game/tools/gui/images/transp_grid.png | Bin 0 -> 208 bytes .../game/tools/gui/images/treeView.png | Bin 0 -> 456 bytes .../tools/gui/images/treeview/default.png | Bin 0 -> 375 bytes .../game/tools/gui/images/treeview/hidden.png | Bin 0 -> 420 bytes .../BaseGame/game/tools/gui/images/upDown.png | Bin 0 -> 3146 bytes .../game/tools/gui/images/uv-editor-btn_d.png | Bin 0 -> 280 bytes .../game/tools/gui/images/uv-editor-btn_h.png | Bin 0 -> 295 bytes .../game/tools/gui/images/uv-editor-btn_n.png | Bin 0 -> 299 bytes .../game/tools/gui/images/visible_d.png | Bin 0 -> 390 bytes .../game/tools/gui/images/visible_h.png | Bin 0 -> 389 bytes .../game/tools/gui/images/visible_i.png | Bin 0 -> 418 bytes .../game/tools/gui/images/visible_n.png | Bin 0 -> 464 bytes .../BaseGame/game/tools/gui/images/window.png | Bin 0 -> 2559 bytes .../game/tools/gui/materialSelector.ed.gui | 2003 +++++++ .../tools/gui/messageBoxes/messageBox.ed.cs | 325 ++ .../gui/messageBoxes/messageBoxOK.ed.gui | 60 + .../gui/messageBoxes/messageBoxOKBuy.ed.gui | 85 + .../messageBoxes/messageBoxOKCancel.ed.gui | 75 + .../messageBoxOKCancelDetailsDlg.ed.gui | 137 + .../gui/messageBoxes/messageBoxYesNo.ed.gui | 75 + .../messageBoxes/messageBoxYesNoCancel.ed.gui | 103 + .../gui/messageBoxes/messagePopup.ed.gui | 46 + .../game/tools/gui/objectSelection.ed.cs | 368 ++ .../game/tools/gui/openFileDialog.ed.cs | 81 + .../BaseGame/game/tools/gui/profilerGraph.cs | 113 + .../BaseGame/game/tools/gui/profilerGraph.gui | 370 ++ .../BaseGame/game/tools/gui/profiles.ed.cs | 1079 ++++ .../game/tools/gui/saveChangesMBDlg.ed.gui | 180 + .../game/tools/gui/saveFileDialog.ed.cs | 48 + .../game/tools/gui/scriptEditorDlg.ed.gui | 210 + .../BaseGame/game/tools/gui/simViewDlg.ed.gui | 348 ++ .../BaseGame/game/tools/gui/uvEditor.ed.gui | 645 ++ .../guiEditor/gui/EditorChooseGUI.ed.gui | 229 + .../game/tools/guiEditor/gui/gridTiny2.PNG | Bin 0 -> 901 bytes .../game/tools/guiEditor/gui/guiEditor.ed.gui | 1535 +++++ .../gui/guiEditorNewGuiDialog.ed.gui | 199 + .../guiEditor/gui/guiEditorPrefsDlg.ed.gui | 181 + .../guiEditor/gui/guiEditorSelectDlg.ed.gui | 566 ++ .../BaseGame/game/tools/guiEditor/main.cs | 57 + .../guiEditor/scripts/EditorChooseGUI.ed.cs | 105 + .../tools/guiEditor/scripts/fileDialogs.ed.cs | 98 + .../tools/guiEditor/scripts/guiEditor.ed.cs | 1189 ++++ .../guiEditor/scripts/guiEditorCanvas.ed.cs | 540 ++ .../scripts/guiEditorContentList.ed.cs | 102 + .../guiEditor/scripts/guiEditorGroup.ed.cs | 149 + .../scripts/guiEditorInspector.ed.cs | 172 + .../scripts/guiEditorNewGuiDialog.ed.cs | 107 + .../guiEditor/scripts/guiEditorPrefsDlg.ed.cs | 85 + .../guiEditor/scripts/guiEditorProfiles.ed.cs | 623 ++ .../scripts/guiEditorSelectDlg.ed.cs | 87 + .../scripts/guiEditorStatusBar.ed.cs | 90 + .../guiEditor/scripts/guiEditorToolbox.ed.cs | 394 ++ .../guiEditor/scripts/guiEditorTreeView.ed.cs | 220 + .../guiEditor/scripts/guiEditorUndo.ed.cs | 598 ++ .../BaseGame/game/tools/levels/BlankRoom.mis | 94 + .../game/tools/levels/BlankRoom_preview.png | Bin 0 -> 12867 bytes Templates/BaseGame/game/tools/main.cs | 284 + .../materialEditor/gui/MaterialToolbar.ed.gui | 68 + .../tools/materialEditor/gui/Profiles.ed.cs | 69 + .../gui/change-material-btn_d.png | Bin 0 -> 759 bytes .../gui/change-material-btn_h.png | Bin 0 -> 758 bytes .../gui/change-material-btn_n.png | Bin 0 -> 668 bytes .../gui/cubeMapEd_cubePreview.max | Bin 0 -> 253952 bytes .../gui/cubeMapEd_previewMat.jpg | Bin 0 -> 306 bytes .../tools/materialEditor/gui/cube_xNeg.jpg | Bin 0 -> 8394 bytes .../tools/materialEditor/gui/cube_xPos.jpg | Bin 0 -> 8907 bytes .../tools/materialEditor/gui/cube_yNeg.jpg | Bin 0 -> 7870 bytes .../tools/materialEditor/gui/cube_yPos.jpg | Bin 0 -> 8272 bytes .../tools/materialEditor/gui/cube_zNeg.jpg | Bin 0 -> 7986 bytes .../tools/materialEditor/gui/cube_zPos.jpg | Bin 0 -> 8582 bytes .../materialEditor/gui/cubemapBtnBorder_d.png | Bin 0 -> 289 bytes .../materialEditor/gui/cubemapBtnBorder_h.png | Bin 0 -> 178 bytes .../materialEditor/gui/cubemapBtnBorder_i.png | Bin 0 -> 189 bytes .../materialEditor/gui/cubemapBtnBorder_n.png | Bin 0 -> 178 bytes .../gui/cubemapEd_spherePreview.max | Bin 0 -> 253952 bytes .../gui/cubemaped_cubepreview.dts | Bin 0 -> 1442 bytes .../gui/cubemaped_cylinderpreview.dts | Bin 0 -> 3618 bytes .../gui/cubemaped_spherepreview.dts | Bin 0 -> 22082 bytes .../gui/cubematEd_cylinderPreview.max | Bin 0 -> 258048 bytes .../tools/materialEditor/gui/cubepreview.dts | Bin 0 -> 1437 bytes .../materialEditor/gui/cylinderpreview.dts | Bin 0 -> 3613 bytes .../tools/materialEditor/gui/gridTiny2.PNG | Bin 0 -> 3017 bytes .../gui/guiMaterialPreviewWindow.ed.gui | 796 +++ .../gui/guiMaterialPropertiesWindow.ed.gui | 4091 +++++++++++++ .../materialEditor/gui/matEd_cubePreview.max | Bin 0 -> 253952 bytes .../gui/matEd_cylinderButt_d.jpg | Bin 0 -> 830 bytes .../gui/matEd_cylinderButt_h.jpg | Bin 0 -> 869 bytes .../gui/matEd_cylinderButt_n.jpg | Bin 0 -> 654 bytes .../gui/matEd_cylinderPreview.max | Bin 0 -> 258048 bytes .../materialEditor/gui/matEd_mappedMat.jpg | Bin 0 -> 409 bytes .../gui/matEd_pyramidPreview.max | Bin 0 -> 253952 bytes .../materialEditor/gui/matEd_sphereButt_d.jpg | Bin 0 -> 891 bytes .../materialEditor/gui/matEd_sphereButt_h.jpg | Bin 0 -> 929 bytes .../materialEditor/gui/matEd_sphereButt_n.jpg | Bin 0 -> 815 bytes .../gui/matEd_spherePreview.max | Bin 0 -> 249856 bytes .../gui/matEd_torusKnotPreview.max | Bin 0 -> 217088 bytes .../materialEditor/gui/matEd_torusPreview.max | Bin 0 -> 217088 bytes .../gui/materialSelectorIcon_d.png | Bin 0 -> 899 bytes .../gui/materialSelectorIcon_h.png | Bin 0 -> 1092 bytes .../gui/materialSelectorIcon_n.png | Bin 0 -> 697 bytes .../gui/mesh-selector-btn_d.png | Bin 0 -> 915 bytes .../gui/mesh-selector-btn_h.png | Bin 0 -> 1098 bytes .../gui/mesh-selector-btn_n.png | Bin 0 -> 720 bytes .../materialEditor/gui/new-material_d.png | Bin 0 -> 834 bytes .../materialEditor/gui/new-material_h.png | Bin 0 -> 793 bytes .../materialEditor/gui/new-material_n.png | Bin 0 -> 830 bytes .../materialEditor/gui/pyramidpreview.dts | Bin 0 -> 1197 bytes .../tools/materialEditor/gui/screenFaded.png | Bin 0 -> 4926 bytes .../tools/materialEditor/gui/scrollBox.jpg | Bin 0 -> 12998 bytes .../materialEditor/gui/spherepreview.dts | Bin 0 -> 22077 bytes .../materialEditor/gui/torusknotpreview.dts | Bin 0 -> 40585 bytes .../materialEditor/gui/torusknowpreview.dts | 0 .../tools/materialEditor/gui/toruspreview.dts | Bin 0 -> 12705 bytes .../tools/materialEditor/gui/unknownImage.png | Bin 0 -> 689 bytes .../tools/materialEditor/gui/unsavedWarn.png | Bin 0 -> 2990 bytes .../tools/materialEditor/gui/wav-none_d.png | Bin 0 -> 384 bytes .../tools/materialEditor/gui/wav-none_h.png | Bin 0 -> 458 bytes .../tools/materialEditor/gui/wav-none_i.png | Bin 0 -> 416 bytes .../tools/materialEditor/gui/wav-none_n.png | Bin 0 -> 479 bytes .../tools/materialEditor/gui/wav-sine_d.png | Bin 0 -> 360 bytes .../tools/materialEditor/gui/wav-sine_h.png | Bin 0 -> 397 bytes .../tools/materialEditor/gui/wav-sine_i.png | Bin 0 -> 373 bytes .../tools/materialEditor/gui/wav-sine_n.png | Bin 0 -> 406 bytes .../tools/materialEditor/gui/wav-square_d.png | Bin 0 -> 258 bytes .../tools/materialEditor/gui/wav-square_h.png | Bin 0 -> 259 bytes .../tools/materialEditor/gui/wav-square_i.png | Bin 0 -> 229 bytes .../tools/materialEditor/gui/wav-square_n.png | Bin 0 -> 259 bytes .../materialEditor/gui/wav-triangle_d.png | Bin 0 -> 352 bytes .../materialEditor/gui/wav-triangle_h.png | Bin 0 -> 398 bytes .../materialEditor/gui/wav-triangle_i.png | Bin 0 -> 372 bytes .../materialEditor/gui/wav-triangle_n.png | Bin 0 -> 415 bytes .../game/tools/materialEditor/main.cs | 160 + .../scripts/materialEditor.ed.cs | 2270 +++++++ .../scripts/materialEditorUndo.ed.cs | 477 ++ .../game/tools/meshRoadEditor/main.cs | 225 + .../tools/meshRoadEditor/meshRoadEditor.cs | 53 + .../tools/meshRoadEditor/meshRoadEditorGui.cs | 256 + .../meshRoadEditor/meshRoadEditorGui.gui | 361 ++ .../meshRoadEditorSettingsTab.gui | 697 +++ .../meshRoadEditor/meshRoadEditorToolbar.gui | 322 + .../images/DefaultHandle.png | Bin 0 -> 179 bytes .../images/mission-area_d.png | Bin 0 -> 973 bytes .../images/mission-area_h.png | Bin 0 -> 1218 bytes .../images/mission-area_n.png | Bin 0 -> 845 bytes .../game/tools/missionAreaEditor/main.cs | 152 + .../missionAreaEditor/missionAreaEditor.ed.cs | 29 + .../missionAreaEditorGui.ed.cs | 84 + .../missionAreaEditorGui.ed.gui | 245 + .../tools/navEditor/CreateNewNavMeshDlg.gui | 392 ++ .../tools/navEditor/NavEditorConsoleDlg.gui | 169 + .../game/tools/navEditor/NavEditorGui.gui | 854 +++ .../tools/navEditor/NavEditorSettingsTab.gui | 506 ++ .../game/tools/navEditor/NavEditorToolbar.gui | 144 + .../BaseGame/game/tools/navEditor/done.wav | Bin 0 -> 36948 bytes .../tools/navEditor/images/nav-cover_d.png | Bin 0 -> 525 bytes .../tools/navEditor/images/nav-cover_h.png | Bin 0 -> 633 bytes .../tools/navEditor/images/nav-cover_n.png | Bin 0 -> 307 bytes .../tools/navEditor/images/nav-editor_d.png | Bin 0 -> 1321 bytes .../tools/navEditor/images/nav-editor_h.png | Bin 0 -> 1595 bytes .../tools/navEditor/images/nav-editor_n.png | Bin 0 -> 1123 bytes .../tools/navEditor/images/nav-link_d.png | Bin 0 -> 692 bytes .../tools/navEditor/images/nav-link_h.png | Bin 0 -> 894 bytes .../tools/navEditor/images/nav-link_n.png | Bin 0 -> 384 bytes .../BaseGame/game/tools/navEditor/main.cs | 274 + .../game/tools/navEditor/navEditor.cs | 360 ++ .../particleEditor/ParticleEditor.ed.gui | 3324 +++++++++++ .../particleEditor/images/play_btn_d.png | Bin 0 -> 450 bytes .../particleEditor/images/play_btn_h.png | Bin 0 -> 492 bytes .../particleEditor/images/play_btn_n.png | Bin 0 -> 401 bytes .../game/tools/particleEditor/main.cs | 178 + .../tools/particleEditor/particleEditor.ed.cs | 255 + .../particleEditor/particleEditorUndo.ed.cs | 606 ++ .../particleEmitterEditor.ed.cs | 659 +++ .../particleParticleEditor.ed.cs | 603 ++ .../BaseGame/game/tools/physicsTools/main.cs | 122 + .../BaseGame/game/tools/resources/.gitignore | 1 + .../game/tools/riverEditor/RiverEditorGui.gui | 412 ++ .../riverEditor/RiverEditorSettingsTab.gui | 517 ++ .../tools/riverEditor/RiverEditorToolbar.gui | 323 + .../BaseGame/game/tools/riverEditor/main.cs | 230 + .../game/tools/riverEditor/riverEditor.cs | 37 + .../game/tools/riverEditor/riverEditorGui.cs | 261 + .../game/tools/roadEditor/RoadEditorGui.gui | 386 ++ .../roadEditor/RoadEditorSettingsTab.gui | 457 ++ .../tools/roadEditor/RoadEditorToolbar.gui | 273 + .../BaseGame/game/tools/roadEditor/main.cs | 215 + .../game/tools/roadEditor/roadEditor.cs | 76 + .../game/tools/roadEditor/roadEditorGui.cs | 249 + Templates/BaseGame/game/tools/settings.xml | 202 + .../game/tools/shapeEditor/gui/Profiles.ed.cs | 52 + .../gui/ShapeEditorSettingsTab.gui | 549 ++ .../shapeEditor/gui/ShapeEditorToolbar.ed.gui | 295 + .../gui/shapeEdAdvancedWindow.ed.gui | 1845 ++++++ .../shapeEditor/gui/shapeEdAnimWindow.ed.gui | 438 ++ .../gui/shapeEdPreviewWindow.ed.gui | 85 + .../shapeEditor/gui/shapeEdPropWindow.ed.gui | 1474 +++++ .../gui/shapeEdSelectWindow.ed.gui | 504 ++ .../tools/shapeEditor/images/back_btn_d.png | Bin 0 -> 458 bytes .../tools/shapeEditor/images/back_btn_h.png | Bin 0 -> 502 bytes .../tools/shapeEditor/images/back_btn_n.png | Bin 0 -> 433 bytes .../shapeEditor/images/collision-shape_d.png | Bin 0 -> 1052 bytes .../shapeEditor/images/collision-shape_h.png | Bin 0 -> 1085 bytes .../shapeEditor/images/collision-shape_n.png | Bin 0 -> 735 bytes .../images/detail-levels_btn_d.png | Bin 0 -> 874 bytes .../images/detail-levels_btn_h.png | Bin 0 -> 1040 bytes .../images/detail-levels_btn_n.png | Bin 0 -> 614 bytes .../tools/shapeEditor/images/fwd_btn_d.png | Bin 0 -> 468 bytes .../tools/shapeEditor/images/fwd_btn_h.png | Bin 0 -> 499 bytes .../tools/shapeEditor/images/fwd_btn_n.png | Bin 0 -> 438 bytes .../tools/shapeEditor/images/ghost_btn_d.png | Bin 0 -> 981 bytes .../tools/shapeEditor/images/ghost_btn_h.png | Bin 0 -> 1168 bytes .../tools/shapeEditor/images/ghost_btn_n.png | Bin 0 -> 683 bytes .../shapeEditor/images/highlight_material.png | Bin 0 -> 513 bytes .../shapeEditor/images/object-bounds_d.png | Bin 0 -> 1094 bytes .../shapeEditor/images/object-bounds_h.png | Bin 0 -> 1230 bytes .../shapeEditor/images/object-bounds_n.png | Bin 0 -> 861 bytes .../images/object-fit-bounds_d.png | Bin 0 -> 1051 bytes .../images/object-fit-bounds_h.png | Bin 0 -> 1173 bytes .../images/object-fit-bounds_n.png | Bin 0 -> 837 bytes .../tools/shapeEditor/images/pause_btn_d.png | Bin 0 -> 493 bytes .../tools/shapeEditor/images/pause_btn_h.png | Bin 0 -> 281 bytes .../tools/shapeEditor/images/pause_btn_n.png | Bin 0 -> 244 bytes .../shapeEditor/images/pingpong_btn_d.png | Bin 0 -> 657 bytes .../shapeEditor/images/pingpong_btn_h.png | Bin 0 -> 596 bytes .../shapeEditor/images/pingpong_btn_n.png | Bin 0 -> 459 bytes .../shapeEditor/images/playbkwd_btn_d.png | Bin 0 -> 527 bytes .../shapeEditor/images/playbkwd_btn_h.png | Bin 0 -> 479 bytes .../shapeEditor/images/playbkwd_btn_n.png | Bin 0 -> 433 bytes .../shapeEditor/images/playfwd_btn_d.png | Bin 0 -> 590 bytes .../shapeEditor/images/playfwd_btn_h.png | Bin 0 -> 492 bytes .../shapeEditor/images/playfwd_btn_n.png | Bin 0 -> 401 bytes .../tools/shapeEditor/images/seq_bar-in_d.png | Bin 0 -> 331 bytes .../tools/shapeEditor/images/seq_bar-in_h.png | Bin 0 -> 331 bytes .../tools/shapeEditor/images/seq_bar-in_n.png | Bin 0 -> 315 bytes .../shapeEditor/images/seq_bar-out_d.png | Bin 0 -> 294 bytes .../shapeEditor/images/seq_bar-out_h.png | Bin 0 -> 293 bytes .../shapeEditor/images/seq_bar-out_n.png | Bin 0 -> 293 bytes .../shapeEditor/images/show-wireframe_d.png | Bin 0 -> 1368 bytes .../shapeEditor/images/show-wireframe_h.png | Bin 0 -> 1611 bytes .../shapeEditor/images/show-wireframe_n.png | Bin 0 -> 989 bytes .../shapeEditor/images/shownodes_btn_d.png | Bin 0 -> 856 bytes .../shapeEditor/images/shownodes_btn_h.png | Bin 0 -> 1186 bytes .../shapeEditor/images/shownodes_btn_n.png | Bin 0 -> 564 bytes .../shapeEditor/images/stepback_btn_d.png | Bin 0 -> 416 bytes .../shapeEditor/images/stepback_btn_h.png | Bin 0 -> 389 bytes .../shapeEditor/images/stepback_btn_n.png | Bin 0 -> 348 bytes .../shapeEditor/images/stepfwd_btn_d.png | Bin 0 -> 404 bytes .../shapeEditor/images/stepfwd_btn_h.png | Bin 0 -> 384 bytes .../shapeEditor/images/stepfwd_btn_n.png | Bin 0 -> 341 bytes .../tools/shapeEditor/images/sun-btn_d.png | Bin 0 -> 688 bytes .../tools/shapeEditor/images/sun-btn_h.png | Bin 0 -> 1128 bytes .../tools/shapeEditor/images/sun-btn_n.png | Bin 0 -> 780 bytes .../shapeEditor/images/transition_slider.png | Bin 0 -> 802 bytes .../shapeEditor/images/trigger_marker.png | Bin 0 -> 136 bytes .../BaseGame/game/tools/shapeEditor/main.cs | 420 ++ .../shapeEditor/scripts/shapeEditor.ed.cs | 3395 +++++++++++ .../scripts/shapeEditorActions.ed.cs | 1304 +++++ .../scripts/shapeEditorHints.ed.cs | 142 + .../worldEditor/gui/AddFMODProjectDlg.ed.gui | 284 + .../gui/AxisGizmoSettingsTab.ed.gui | 524 ++ .../worldEditor/gui/CameraSettingsTab.ed.gui | 429 ++ .../gui/EditorChooseLevelGui.ed.gui | 269 + .../tools/worldEditor/gui/EditorGui.ed.gui | 1304 +++++ .../gui/EditorSettingsWindow.ed.gui | 122 + .../worldEditor/gui/GeneralSettingsTab.ed.gui | 284 + .../gui/GenericPromptDialog.ed.gui | 115 + .../gui/ManageBookmarksWindow.ed.gui | 145 + .../gui/ManageSFXParametersWindow.ed.gui | 240 + .../gui/ObjectEditorSettingsTab.ed.gui | 1093 ++++ .../gui/ObjectSnapOptionsWindow.ed.gui | 871 +++ .../gui/ProceduralTerrainPainterGui.gui | 405 ++ .../gui/SelectObjectsWindow.ed.gui | 566 ++ .../gui/TerrainBrushSoftnessCurveDlg.ed.gui | 232 + .../worldEditor/gui/TerrainEditToolbar.ed.gui | 615 ++ .../gui/TerrainEditorSettingsTab.ed.gui | 301 + .../gui/TerrainEditorVSettingsGui.ed.gui | 276 + .../gui/TerrainPainterToolbar.ed.gui | 637 ++ .../gui/TerrainPainterWindow.ed.gui | 249 + .../worldEditor/gui/TimeAdjustGui.ed.gui | 214 + .../ConvexEditorPalette.ed.gui | 102 + .../DecalEditorPalette.ed.gui | 121 + .../ForestEditorPalette.ed.gui | 163 + .../MeshRoadEditorPalette.ed.gui | 163 + .../NavEditorPalette.ed.gui | 130 + .../RiverEditorPalette.ed.gui | 163 + .../RoadEditorPalette.ed.gui | 144 + .../ShapeEditorPalette.ed.gui | 102 + .../TerrainEditPalette.ed.gui | 235 + .../TerrainPainterPalette.ed.gui | 14 + .../WorldEditorPalette.ed.gui | 98 + .../gui/ToolsPaletteGroups/init.cs | 102 + .../worldEditor/gui/ToolsPaletteWindow.ed.gui | 68 + .../tools/worldEditor/gui/ToolsToolbar.ed.gui | 72 + .../gui/TransformSelectionWindow.ed.gui | 1026 ++++ .../gui/VisibilityLayerWindow.ed.gui | 278 + .../gui/WorldEditorInspectorWindow.ed.gui | 143 + .../worldEditor/gui/WorldEditorToolbar.ed.gui | 673 +++ .../gui/WorldEditorTreeWindow.ed.gui | 577 ++ .../gui/guiCreateNewTerrainGui.gui | 352 ++ .../gui/guiTerrainEditorToolbar.ed.gui | 0 .../worldEditor/gui/guiTerrainExportGui.gui | 310 + .../worldEditor/gui/guiTerrainImportGui.gui | 759 +++ .../gui/guiTerrainMaterialDlg.ed.gui | 1392 +++++ .../gui/guiWorldEditorCreatorWindow.ed.gui | 286 + .../gui/guiWorldEditorMissionInspector.ed.gui | 299 + .../worldEditor/gui/objectBuilderGui.ed.gui | 1156 ++++ .../game/tools/worldEditor/gui/profiles.ed.cs | 126 + .../tools/worldEditor/images/CUR_3darrow.png | Bin 0 -> 604 bytes .../worldEditor/images/CUR_3ddiagleft.png | Bin 0 -> 842 bytes .../worldEditor/images/CUR_3ddiagright.png | Bin 0 -> 847 bytes .../worldEditor/images/CUR_3dleftright.png | Bin 0 -> 607 bytes .../tools/worldEditor/images/CUR_3dupdown.png | Bin 0 -> 630 bytes .../tools/worldEditor/images/CUR_grab.png | Bin 0 -> 769 bytes .../tools/worldEditor/images/CUR_hand.png | Bin 0 -> 884 bytes .../tools/worldEditor/images/CUR_rotate.png | Bin 0 -> 1145 bytes .../worldEditor/images/DefaultHandle.png | Bin 0 -> 179 bytes .../tools/worldEditor/images/LockedHandle.png | Bin 0 -> 340 bytes .../tools/worldEditor/images/SelectHandle.png | Bin 0 -> 179 bytes .../tools/worldEditor/images/boxBrush_d.PNG | Bin 0 -> 429 bytes .../tools/worldEditor/images/boxBrush_h.PNG | Bin 0 -> 611 bytes .../tools/worldEditor/images/boxBrush_n.PNG | Bin 0 -> 246 bytes .../images/brushAdjustHeight_d.PNG | Bin 0 -> 755 bytes .../images/brushAdjustHeight_h.PNG | Bin 0 -> 877 bytes .../images/brushAdjustHeight_n.png | Bin 0 -> 780 bytes .../worldEditor/images/brushPaintNoise_d.png | Bin 0 -> 791 bytes .../worldEditor/images/brushPaintNoise_h.png | Bin 0 -> 895 bytes .../worldEditor/images/brushPaintNoise_n.png | Bin 0 -> 791 bytes .../images/brushThermalErosion.png | Bin 0 -> 841 bytes .../images/brushThermalErosion_d.png | Bin 0 -> 847 bytes .../images/brushThermalErosion_h.png | Bin 0 -> 848 bytes .../worldEditor/images/circleBrush_d.PNG | Bin 0 -> 810 bytes .../worldEditor/images/circleBrush_h.PNG | Bin 0 -> 1049 bytes .../worldEditor/images/circleBrush_n.PNG | Bin 0 -> 573 bytes .../tools/worldEditor/images/clearEmpty_d.PNG | Bin 0 -> 681 bytes .../tools/worldEditor/images/clearEmpty_h.PNG | Bin 0 -> 760 bytes .../tools/worldEditor/images/clearEmpty_n.PNG | Bin 0 -> 636 bytes .../worldEditor/images/flattenHeight_d.PNG | Bin 0 -> 434 bytes .../worldEditor/images/flattenHeight_h.PNG | Bin 0 -> 494 bytes .../worldEditor/images/flattenHeight_n.PNG | Bin 0 -> 439 bytes .../worldEditor/images/lowerHeight_d.PNG | Bin 0 -> 757 bytes .../worldEditor/images/lowerHeight_h.PNG | Bin 0 -> 859 bytes .../worldEditor/images/lowerHeight_n.PNG | Bin 0 -> 728 bytes .../tools/worldEditor/images/maskBrush_d.PNG | Bin 0 -> 777 bytes .../tools/worldEditor/images/maskBrush_h.PNG | Bin 0 -> 982 bytes .../tools/worldEditor/images/maskBrush_n.PNG | Bin 0 -> 822 bytes .../worldEditor/images/raiseHeight_d.PNG | Bin 0 -> 697 bytes .../worldEditor/images/raiseHeight_h.PNG | Bin 0 -> 792 bytes .../worldEditor/images/raiseHeight_n.PNG | Bin 0 -> 728 bytes .../images/road-river/add-mesh-road_d.png | Bin 0 -> 855 bytes .../images/road-river/add-mesh-road_h.png | Bin 0 -> 1072 bytes .../images/road-river/add-mesh-road_n.png | Bin 0 -> 649 bytes .../images/road-river/add-point_d.png | Bin 0 -> 760 bytes .../images/road-river/add-point_h.png | Bin 0 -> 978 bytes .../images/road-river/add-point_n.png | Bin 0 -> 519 bytes .../images/road-river/add-river_d.png | Bin 0 -> 1176 bytes .../images/road-river/add-river_h.png | Bin 0 -> 1396 bytes .../images/road-river/add-river_n.png | Bin 0 -> 1106 bytes .../images/road-river/add-road-path_d.png | Bin 0 -> 915 bytes .../images/road-river/add-road-path_h.png | Bin 0 -> 1129 bytes .../images/road-river/add-road-path_n.png | Bin 0 -> 715 bytes .../road-river/menubar/show-spline_d.png | Bin 0 -> 783 bytes .../road-river/menubar/show-spline_h.png | Bin 0 -> 976 bytes .../road-river/menubar/show-spline_n.png | Bin 0 -> 553 bytes .../road-river/menubar/show-texture_d.png | Bin 0 -> 1077 bytes .../road-river/menubar/show-texture_h.png | Bin 0 -> 1297 bytes .../road-river/menubar/show-texture_n.png | Bin 0 -> 852 bytes .../road-river/menubar/show-wireframe_d.png | Bin 0 -> 1074 bytes .../road-river/menubar/show-wireframe_h.png | Bin 0 -> 1283 bytes .../road-river/menubar/show-wireframe_n.png | Bin 0 -> 814 bytes .../images/road-river/move-point_d.png | Bin 0 -> 571 bytes .../images/road-river/move-point_h.png | Bin 0 -> 960 bytes .../images/road-river/move-point_n.png | Bin 0 -> 560 bytes .../images/road-river/rotate-point_d.png | Bin 0 -> 629 bytes .../images/road-river/rotate-point_h.png | Bin 0 -> 1139 bytes .../images/road-river/rotate-point_n.png | Bin 0 -> 563 bytes .../images/road-river/scale-point_d.png | Bin 0 -> 551 bytes .../images/road-river/scale-point_h.png | Bin 0 -> 1032 bytes .../images/road-river/scale-point_n.png | Bin 0 -> 574 bytes .../images/road-river/subtract-point_d.png | Bin 0 -> 740 bytes .../images/road-river/subtract-point_h.png | Bin 0 -> 924 bytes .../images/road-river/subtract-point_n.png | Bin 0 -> 499 bytes .../tools/worldEditor/images/setEmpty_d.PNG | Bin 0 -> 779 bytes .../tools/worldEditor/images/setEmpty_h.PNG | Bin 0 -> 841 bytes .../tools/worldEditor/images/setEmpty_n.PNG | Bin 0 -> 701 bytes .../tools/worldEditor/images/setHeight_d.PNG | Bin 0 -> 718 bytes .../tools/worldEditor/images/setHeight_h.PNG | Bin 0 -> 858 bytes .../tools/worldEditor/images/setHeight_n.PNG | Bin 0 -> 698 bytes .../worldEditor/images/smoothHeight_d.PNG | Bin 0 -> 476 bytes .../worldEditor/images/smoothHeight_h.PNG | Bin 0 -> 540 bytes .../worldEditor/images/smoothHeight_n.PNG | Bin 0 -> 410 bytes .../tools/worldEditor/images/softCurve_d.PNG | Bin 0 -> 599 bytes .../tools/worldEditor/images/softCurve_h.PNG | Bin 0 -> 783 bytes .../tools/worldEditor/images/softCurve_n.PNG | Bin 0 -> 483 bytes .../images/terrainpainter/new_layer_icon.png | Bin 0 -> 383 bytes .../terrain-painter-border-large.png | Bin 0 -> 529 bytes .../terrain-painter-border-new_h.png | Bin 0 -> 573 bytes .../terrain-painter-border-new_n.png | Bin 0 -> 443 bytes .../terrain-painter-border_d.png | Bin 0 -> 1049 bytes .../terrain-painter-border_h.png | Bin 0 -> 369 bytes .../terrain-painter-border_n.png | Bin 0 -> 381 bytes .../images/toolbar/3rd-person-camera_d.png | Bin 0 -> 850 bytes .../images/toolbar/3rd-person-camera_h.png | Bin 0 -> 1040 bytes .../images/toolbar/3rd-person-camera_n.png | Bin 0 -> 654 bytes .../worldEditor/images/toolbar/camera_d.png | Bin 0 -> 776 bytes .../worldEditor/images/toolbar/camera_h.png | Bin 0 -> 891 bytes .../worldEditor/images/toolbar/camera_n.png | Bin 0 -> 722 bytes .../images/toolbar/datablock-editor_d.png | Bin 0 -> 1222 bytes .../images/toolbar/datablock-editor_h.png | Bin 0 -> 1377 bytes .../images/toolbar/datablock-editor_n.png | Bin 0 -> 1056 bytes .../tools/worldEditor/images/toolbar/gui.png | Bin 0 -> 350 bytes .../worldEditor/images/toolbar/gui_d.png | Bin 0 -> 433 bytes .../worldEditor/images/toolbar/gui_h.png | Bin 0 -> 549 bytes .../images/toolbar/matterial-editor_d.png | Bin 0 -> 1083 bytes .../images/toolbar/matterial-editor_h.png | Bin 0 -> 1263 bytes .../images/toolbar/matterial-editor_n.png | Bin 0 -> 1004 bytes .../images/toolbar/mesh-road-editor_d.png | Bin 0 -> 901 bytes .../images/toolbar/mesh-road-editor_h.png | Bin 0 -> 1077 bytes .../images/toolbar/mesh-road-editor_n.png | Bin 0 -> 619 bytes .../images/toolbar/missionarea-editor_d.png | Bin 0 -> 936 bytes .../images/toolbar/missionarea-editor_h.png | Bin 0 -> 1102 bytes .../images/toolbar/missionarea-editor_n.png | Bin 0 -> 751 bytes .../images/toolbar/paint-terrain_d.png | Bin 0 -> 808 bytes .../images/toolbar/paint-terrain_h.png | Bin 0 -> 1010 bytes .../images/toolbar/paint-terrain_n.png | Bin 0 -> 807 bytes .../images/toolbar/particleeditor_d.png | Bin 0 -> 1070 bytes .../images/toolbar/particleeditor_h.png | Bin 0 -> 1237 bytes .../images/toolbar/particleeditor_n.png | Bin 0 -> 914 bytes .../images/toolbar/playbutton_d.png | Bin 0 -> 1503 bytes .../images/toolbar/playbutton_h.png | Bin 0 -> 1495 bytes .../images/toolbar/playbutton_n.png | Bin 0 -> 1266 bytes .../worldEditor/images/toolbar/player_d.png | Bin 0 -> 701 bytes .../worldEditor/images/toolbar/player_h.png | Bin 0 -> 795 bytes .../worldEditor/images/toolbar/player_n.png | Bin 0 -> 659 bytes .../images/toolbar/river-editor_d.png | Bin 0 -> 1227 bytes .../images/toolbar/river-editor_h.png | Bin 0 -> 1424 bytes .../images/toolbar/river-editor_n.png | Bin 0 -> 886 bytes .../images/toolbar/road-path-editor_d.png | Bin 0 -> 977 bytes .../images/toolbar/road-path-editor_h.png | Bin 0 -> 1148 bytes .../images/toolbar/road-path-editor_n.png | Bin 0 -> 709 bytes .../images/toolbar/sculpt-terrain_d.png | Bin 0 -> 936 bytes .../images/toolbar/sculpt-terrain_h.png | Bin 0 -> 1102 bytes .../images/toolbar/sculpt-terrain_n.png | Bin 0 -> 751 bytes .../images/toolbar/shape-editor_d.png | Bin 0 -> 1237 bytes .../images/toolbar/shape-editor_h.png | Bin 0 -> 1446 bytes .../images/toolbar/shape-editor_n.png | Bin 0 -> 1006 bytes .../images/toolbar/transform-objects_d.png | Bin 0 -> 1020 bytes .../images/toolbar/transform-objects_h.png | Bin 0 -> 1196 bytes .../images/toolbar/transform-objects_n.png | Bin 0 -> 1058 bytes .../worldEditor/images/toolbar/world.png | Bin 0 -> 807 bytes .../worldEditor/images/toolbar/world_d.png | Bin 0 -> 983 bytes .../worldEditor/images/toolbar/world_h.png | Bin 0 -> 1185 bytes .../BaseGame/game/tools/worldEditor/main.cs | 136 + .../scripts/AddFMODProjectDlg.ed.cs | 253 + .../scripts/EditorChooseLevelGui.ed.cs | 155 + .../tools/worldEditor/scripts/EditorGui.ed.cs | 2825 +++++++++ .../scripts/ManageSFXParametersWindow.ed.cs | 847 +++ .../scripts/SelectObjectsWindow.ed.cs | 178 + .../worldEditor/scripts/cameraBookmarks.ed.cs | 360 ++ .../worldEditor/scripts/cameraCommands.ed.cs | 200 + .../tools/worldEditor/scripts/cursors.ed.cs | 79 + .../worldEditor/scripts/editor.bind.ed.cs | 84 + .../tools/worldEditor/scripts/editor.ed.cs | 201 + .../worldEditor/scripts/editor.keybinds.cs | 80 + .../scripts/editorInputCommands.cs | 181 + .../worldEditor/scripts/editorPlugin.ed.cs | 203 + .../worldEditor/scripts/editorPrefs.ed.cs | 435 ++ .../worldEditor/scripts/editorRender.ed.cs | 58 + .../scripts/editorSettingsWindow.ed.cs | 142 + .../worldEditor/scripts/editors/creator.ed.cs | 823 +++ .../scripts/editors/missionArea.ed.cs | 30 + .../scripts/editors/terrainEditor.ed.cs | 479 ++ .../scripts/editors/worldEditor.ed.cs | 486 ++ .../scripts/interfaces/levelInfoEditor.ed.cs | 27 + .../scripts/interfaces/simObjectEditor.ed.cs | 26 + .../interfaces/terrainMaterialDlg.ed.cs | 625 ++ .../tools/worldEditor/scripts/lighting.ed.cs | 63 + .../worldEditor/scripts/menuHandlers.ed.cs | 906 +++ .../tools/worldEditor/scripts/menus.ed.cs | 425 ++ .../scripts/objectSnapOptions.ed.cs | 95 + .../scripts/transformSelection.ed.cs | 448 ++ .../worldEditor/scripts/undoManager.ed.cs | 147 + .../worldEditor/scripts/visibilityLayer.ed.cs | 201 + Templates/BaseGame/source/readme.txt | 7 + Templates/BaseGame/source/torqueConfig.h | 246 + Templates/BaseGame/thumb.png | Bin 0 -> 51416 bytes Templates/Empty/game/core/main.cs | 6 + .../core/scripts/client/postFx/GammaPostFX.cs | 1 + .../game/core/scripts/client/postFx/MLAA.cs | 6 +- .../core/scripts/client/postFx/caustics.cs | 4 +- .../game/core/scripts/client/postFx/dof.cs | 8 +- .../game/core/scripts/client/postFx/ssao.cs | 2 +- Templates/Full/game/Full.torsion.opt | 14 + Templates/Full/game/core/main.cs | 6 + .../game/core/scripts/client/postFx/MLAA.cs | 2 +- .../core/scripts/client/postFx/caustics.cs | 4 +- .../game/core/scripts/client/postFx/ssao.cs | 2 +- Tools/CMake/template.torsion.in | 5 +- 1572 files changed, 146699 insertions(+), 85 deletions(-) create mode 100644 Templates/BaseGame/BaseGame.cmake create mode 100644 Templates/BaseGame/DeleteCachedDTSs.bat create mode 100644 Templates/BaseGame/DeleteCachedDTSs.command create mode 100644 Templates/BaseGame/DeleteDSOs.bat create mode 100644 Templates/BaseGame/DeleteDSOs.command create mode 100644 Templates/BaseGame/DeletePrefs.bat create mode 100644 Templates/BaseGame/DeletePrefs.command create mode 100644 Templates/BaseGame/cleanShaders.bat create mode 100644 Templates/BaseGame/cleanShaders.command create mode 100644 Templates/BaseGame/game/Template.torsion.exports create mode 100644 Templates/BaseGame/game/core/audio.cs create mode 100644 Templates/BaseGame/game/core/canvas.cs create mode 100644 Templates/BaseGame/game/core/console/console.gui create mode 100644 Templates/BaseGame/game/core/console/main.cs create mode 100644 Templates/BaseGame/game/core/console/profiles.cs create mode 100644 Templates/BaseGame/game/core/cursor.cs create mode 100644 Templates/BaseGame/game/core/fonts/Arial 10 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial 12 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial 14 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial 16 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial 36 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial Bold 14 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial Bold 16 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Arial Bold 18 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/ArialBold 14 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/ArialItalic 14 (ansi).uft create mode 100644 Templates/BaseGame/game/core/fonts/Lucida Console 12 (ansi).uft create mode 100644 Templates/BaseGame/game/core/gfxData/clouds.cs create mode 100644 Templates/BaseGame/game/core/gfxData/commonMaterialData.cs create mode 100644 Templates/BaseGame/game/core/gfxData/scatterSky.cs create mode 100644 Templates/BaseGame/game/core/gfxData/shaders.cs create mode 100644 Templates/BaseGame/game/core/gfxData/terrainBlock.cs create mode 100644 Templates/BaseGame/game/core/gfxData/water.cs create mode 100644 Templates/BaseGame/game/core/gfxprofile/D3D9.ATITechnologiesInc.cs create mode 100644 Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.GeForce8600.cs create mode 100644 Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.QuadroFXGo1000.cs create mode 100644 Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.cs create mode 100644 Templates/BaseGame/game/core/gfxprofile/D3D9.cs create mode 100644 Templates/BaseGame/game/core/globals.cs create mode 100644 Templates/BaseGame/game/core/helperFunctions.cs create mode 100644 Templates/BaseGame/game/core/images/button.png create mode 100644 Templates/BaseGame/game/core/images/checkbox.png create mode 100644 Templates/BaseGame/game/core/images/group-border.png create mode 100644 Templates/BaseGame/game/core/images/inactive-overlay.png create mode 100644 Templates/BaseGame/game/core/images/loadingbar.png create mode 100644 Templates/BaseGame/game/core/images/materials.cs create mode 100644 Templates/BaseGame/game/core/images/missingTexture.png create mode 100644 Templates/BaseGame/game/core/images/scrollBar.png create mode 100644 Templates/BaseGame/game/core/images/textEdit.png create mode 100644 Templates/BaseGame/game/core/images/thumbHighlightButton.png create mode 100644 Templates/BaseGame/game/core/images/unavailable.png create mode 100644 Templates/BaseGame/game/core/images/warnMat.dds create mode 100644 Templates/BaseGame/game/core/images/window.png create mode 100644 Templates/BaseGame/game/core/lighting.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/deferredShading.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/depthviz.png create mode 100644 Templates/BaseGame/game/core/lighting/advanced/init.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/lightViz.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/shaders.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/shadowViz.cs create mode 100644 Templates/BaseGame/game/core/lighting/advanced/shadowViz.gui create mode 100644 Templates/BaseGame/game/core/lighting/basic/init.cs create mode 100644 Templates/BaseGame/game/core/lighting/basic/shadowFilter.cs create mode 100644 Templates/BaseGame/game/core/lighting/shadowMaps/init.cs create mode 100644 Templates/BaseGame/game/core/main.cs create mode 100644 Templates/BaseGame/game/core/parseArgs.cs create mode 100644 Templates/BaseGame/game/core/postFx.cs create mode 100644 Templates/BaseGame/game/core/profiles.cs create mode 100644 Templates/BaseGame/game/core/renderManager.cs create mode 100644 Templates/BaseGame/game/core/sfx/audioAmbience.cs create mode 100644 Templates/BaseGame/game/core/sfx/audioData.cs create mode 100644 Templates/BaseGame/game/core/sfx/audioDescriptions.cs create mode 100644 Templates/BaseGame/game/core/sfx/audioEnvironments.cs create mode 100644 Templates/BaseGame/game/core/sfx/audioStates.cs create mode 100644 Templates/BaseGame/game/data/clientServer/ClientServer.cs create mode 100644 Templates/BaseGame/game/data/clientServer/ClientServer.module create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/client/client.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/client/connectionToServer.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/client/levelDownload.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/client/levelLoad.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/client/message.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/audio.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/commands.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/connectionToClient.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/defaults.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/levelDownload.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/levelInfo.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/levelLoad.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/message.cs create mode 100644 Templates/BaseGame/game/data/clientServer/scripts/server/server.cs create mode 100644 Templates/BaseGame/game/data/defaults.cs create mode 100644 Templates/BaseGame/game/data/postFX/art/AreaMap33.dds create mode 100644 Templates/BaseGame/game/data/postFX/art/caustics_1.png create mode 100644 Templates/BaseGame/game/data/postFX/art/caustics_2.png create mode 100644 Templates/BaseGame/game/data/postFX/art/noise.png create mode 100644 Templates/BaseGame/game/data/postFX/art/null_color_ramp.png create mode 100644 Templates/BaseGame/game/data/postFX/postFX.cs create mode 100644 Templates/BaseGame/game/data/postFX/postFX.module create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/GammaPostFX.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/MLAA.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/MotionBlurFx.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/caustics.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/chromaticLens.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/default.postfxpreset.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/dof.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/edgeAA.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/flash.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/fog.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/fxaa.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/glow.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/hdr.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/lightRay.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/ovrBarrelDistortion.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.settings.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.persistance.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/ssao.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/turbulence.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/client/vignette.cs create mode 100644 Templates/BaseGame/game/data/postFX/scripts/gui/postFxManager.gui create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogRefl.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogRefl.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/basicCloudsP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/basicCloudsV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/cloudLayerP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/cloudLayerV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/colorP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/colorV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/textureP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fixedFunction/textureV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/foliage.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/basicCloudsP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/basicCloudsV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/blurP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/blurV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/cloudLayerP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/cloudLayerV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/foliage.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/guiMaterialV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/hlslCompat.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/imposter.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/lighting.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/particleCompositeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/particleCompositeV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/particlesP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/particlesV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/planarReflectP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/planarReflectV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/precipP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/precipV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/projectedShadowP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/projectedShadowV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/scatterSkyP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/scatterSkyV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/torque.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/wavesP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/gl/wind.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/guiMaterialV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/hlslStructs.h create mode 100644 Templates/BaseGame/game/data/shaders/common/hlslStructs.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/imposter.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/convexGeometryV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgColorBufferP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgGlowVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgShadowVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgSpecMapVisualizeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredColorShaderP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredShadingP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuad.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuadV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/convexGeometryV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgColorBufferP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgGlowVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgSpecMapVisualizeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredColorShaderP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredShadingP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/lightingUtils.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/pointLightP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/softShadow.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/spotLightP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/vectorLightP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/lightingUtils.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/pointLightP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/softShadow.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/spotLightP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/advanced/vectorLightP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO.h create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_GLSL.h create mode 100644 Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_HLSL.h create mode 100644 Templates/BaseGame/game/data/shaders/common/particleCompositeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/particleCompositeV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/particlesP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/particlesV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/planarReflectBumpP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/planarReflectBumpV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/planarReflectP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/planarReflectV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/VolFogGlowP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/caustics/causticsP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/caustics/gl/causticsP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/chromaticLens.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Passthrough_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Passthrough_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeDetectP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/flashP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fogP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fxaa/Fxaa3_11.h create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gammaP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/VolFogGlowP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/chromaticLens.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/flashP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/fogP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/gammaP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/motionBlurP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/passthruP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/postFX.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/postFxV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/turbulenceP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/gl/underwaterFogP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/glowBlurP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/glowBlurV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/brightPassFilterP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/finalPassCombineP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/brightPassFilterP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/luminanceVisP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/luminanceVisP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumInitialP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumIterativeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/edgeDetectionP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/functions.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/functions.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/offsetV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/passthruV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/offsetV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/mlaa/passthruV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/motionBlurP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/barrelDistortionChromaP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/barrelDistortionP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/gl/barrelDistortionChromaP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/gl/barrelDistortionP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/gl/monoToStereoP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/oculusvr/monoToStereoP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/passthruP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/postFx.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/postFxV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/postFxV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_Blur_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_Blur_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/turbulenceP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/underwaterFogP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/vignette/VignetteP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/postFx/vignette/gl/VignetteP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/precipP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/precipV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/projectedShadowP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/projectedShadowV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/scatterSkyP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/scatterSkyV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/shaderModel.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/shaderModelAutoGen.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/shdrConsts.h create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/blendP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/blendV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/gl/blendP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/gl/blendV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/terrain.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/terrain/terrain.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/torque.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/gl/waterP.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/gl/waterV.glsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/waterBasicP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/waterBasicV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/waterP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/water/waterV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/wavesP.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/wavesV.hlsl create mode 100644 Templates/BaseGame/game/data/shaders/common/wind.hlsl create mode 100644 Templates/BaseGame/game/data/splash.png create mode 100644 Templates/BaseGame/game/data/torque.png create mode 100644 Templates/BaseGame/game/data/ui/UI.cs create mode 100644 Templates/BaseGame/game/data/ui/UI.module create mode 100644 Templates/BaseGame/game/data/ui/art/ScreenBrightness_Dark.png create mode 100644 Templates/BaseGame/game/data/ui/art/ScreenBrightness_Light.png create mode 100644 Templates/BaseGame/game/data/ui/art/Torque-3D-logo-shortcut.png create mode 100644 Templates/BaseGame/game/data/ui/art/Torque-3D-logo-w.png create mode 100644 Templates/BaseGame/game/data/ui/art/Torque-3D-logo.png create mode 100644 Templates/BaseGame/game/data/ui/art/Torque-3D-logo_alt.png create mode 100644 Templates/BaseGame/game/data/ui/art/background-dark.png create mode 100644 Templates/BaseGame/game/data/ui/art/background.png create mode 100644 Templates/BaseGame/game/data/ui/art/buttontab.png create mode 100644 Templates/BaseGame/game/data/ui/art/chatHudBorderArray.png create mode 100644 Templates/BaseGame/game/data/ui/art/checkbox.png create mode 100644 Templates/BaseGame/game/data/ui/art/clear-btn_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/clear-btn_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/clear-btn_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/collapse-toolbar_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/collapse-toolbar_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/collapse-toolbar_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/defaultCursor.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropDown.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropdown-button-arrow.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropdown-textEdit.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropslider_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropslider_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/dropslider_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/expand-toolbar_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/expand-toolbar_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/expand-toolbar_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/folder.png create mode 100644 Templates/BaseGame/game/data/ui/art/group-border.png create mode 100644 Templates/BaseGame/game/data/ui/art/hudfill.png create mode 100644 Templates/BaseGame/game/data/ui/art/inactive-overlay.png create mode 100644 Templates/BaseGame/game/data/ui/art/lagIcon.png create mode 100644 Templates/BaseGame/game/data/ui/art/loadingbar.png create mode 100644 Templates/BaseGame/game/data/ui/art/macCursor.png create mode 100644 Templates/BaseGame/game/data/ui/art/menu-button.png create mode 100644 Templates/BaseGame/game/data/ui/art/menu.png create mode 100644 Templates/BaseGame/game/data/ui/art/menuSlider.png create mode 100644 Templates/BaseGame/game/data/ui/art/new_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/new_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/new_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/next-button_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/next-button_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/next-button_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/no-preview.png create mode 100644 Templates/BaseGame/game/data/ui/art/numericslider.png create mode 100644 Templates/BaseGame/game/data/ui/art/previous-button_d.png create mode 100644 Templates/BaseGame/game/data/ui/art/previous-button_h.png create mode 100644 Templates/BaseGame/game/data/ui/art/previous-button_n.png create mode 100644 Templates/BaseGame/game/data/ui/art/radioButton.png create mode 100644 Templates/BaseGame/game/data/ui/art/scrollBar.png create mode 100644 Templates/BaseGame/game/data/ui/art/selector-button-blank.png create mode 100644 Templates/BaseGame/game/data/ui/art/selector-button-dark.png create mode 100644 Templates/BaseGame/game/data/ui/art/selector-button-highlight-only.png create mode 100644 Templates/BaseGame/game/data/ui/art/selector-button.png create mode 100644 Templates/BaseGame/game/data/ui/art/separator-h.png create mode 100644 Templates/BaseGame/game/data/ui/art/separator-v.png create mode 100644 Templates/BaseGame/game/data/ui/art/slider-w-box.png create mode 100644 Templates/BaseGame/game/data/ui/art/slider.png create mode 100644 Templates/BaseGame/game/data/ui/art/tab-border.png create mode 100644 Templates/BaseGame/game/data/ui/art/tab.png create mode 100644 Templates/BaseGame/game/data/ui/art/textEdit.png create mode 100644 Templates/BaseGame/game/data/ui/art/textEditSliderBox.png create mode 100644 Templates/BaseGame/game/data/ui/art/window.png create mode 100644 Templates/BaseGame/game/data/ui/scripts/chooseLevelDlg.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/controlsMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/datablocks/guiSounds.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/graphicsMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/RecordingsDlg.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/chooseLevelDlg.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/controlsMenuSetting.taml create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsCtrl.taml create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsSlider.taml create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/joinServerMenu.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/loadingGui.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/mainMenu.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/messageBoxOK.ed.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/messageBoxYesNo.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/netGraphGui.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/optionsDlg.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/optionsMenu.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/pauseMenu.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/profiler.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/remapConfirmDlg.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/guis/remapDlg.gui create mode 100644 Templates/BaseGame/game/data/ui/scripts/joinServerMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/mainMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/messageBoxes.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/optionsList.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/optionsMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/pauseMenu.cs create mode 100644 Templates/BaseGame/game/data/ui/scripts/profiles.cs create mode 100644 Templates/BaseGame/game/data/ui/sound/buttonClick.wav create mode 100644 Templates/BaseGame/game/data/ui/sound/buttonHover.wav create mode 100644 Templates/BaseGame/game/main.cs.in create mode 100644 Templates/BaseGame/game/runTests.cs create mode 100644 Templates/BaseGame/game/tools/base/canvas/baseCanvas.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/main.cs create mode 100644 Templates/BaseGame/game/tools/base/menuBar/baseMenu.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/menuBar/fileMenu.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/utils/inspector.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/utils/objectNameValidation.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/utils/swatchButtons.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/utils/treeViewFilterCtrls.ed.cs create mode 100644 Templates/BaseGame/game/tools/base/utils/undoActions.ed.cs create mode 100644 Templates/BaseGame/game/tools/classIcons/BasicClouds.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Camera.png create mode 100644 Templates/BaseGame/game/tools/classIcons/CameraBookmark.png create mode 100644 Templates/BaseGame/game/tools/classIcons/CloudLayer.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ConvexShape.png create mode 100644 Templates/BaseGame/game/tools/classIcons/CreatorTree.png create mode 100644 Templates/BaseGame/game/tools/classIcons/DecalRoad.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Forest.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ForestBrush.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ForestBrushElement.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GameTSCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GroundCover.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GroundPlane.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiAutoScrollCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiBitmapBorderCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiBitmapButtonCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiBitmapButtonTextCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiBitmapCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiBorderButtonCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiButtonCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiCheckBoxCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiColorPickerCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiContainer.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiControl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiControlArrayControl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiCrossHairHud.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiDecoyCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiDragAndDropControl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiDynamicCtrlArrayControl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiFadeinBitmapCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiFileTreeCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiFilterCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiFormCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiFrameSetCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiGradientSwatchCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiGraphCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiHealthBarHud.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiIconButtonCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiListBoxCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiMLTextCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiMLTextEditCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiMenuBar.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiObjectView.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiPanel.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiPopUpMenuCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiPopUpMenuCtrlEx.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiProgressBitmapCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiProgressCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiRadioCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiRectHandles.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiRolloutCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiScrollCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiSplitContainer.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiStackControl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiSwatchButtonCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTabBookCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTabPageCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTextCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTextEditCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTextEditSliderCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTextListCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTheoraCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiTreeViewCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiWindowCollapseCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/GuiWindowCtrl.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Item.png create mode 100644 Templates/BaseGame/game/tools/classIcons/LevelInfo.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Lightning.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Marker.png create mode 100644 Templates/BaseGame/game/tools/classIcons/MeshRoad.png create mode 100644 Templates/BaseGame/game/tools/classIcons/MissionArea.png create mode 100644 Templates/BaseGame/game/tools/classIcons/NavMesh.png create mode 100644 Templates/BaseGame/game/tools/classIcons/NavPath.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ParticleEmitter.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ParticleEmitterNode.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Path.png create mode 100644 Templates/BaseGame/game/tools/classIcons/PhysicalZone.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Player.png create mode 100644 Templates/BaseGame/game/tools/classIcons/PointLight.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Portal.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Precipitation.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Prefab.png create mode 100644 Templates/BaseGame/game/tools/classIcons/PxCloth.png create mode 100644 Templates/BaseGame/game/tools/classIcons/River.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SFXEmitter.png create mode 100644 Templates/BaseGame/game/tools/classIcons/ScatterSky.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SceneObject.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SimDataBlock.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SimObject.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SimSet.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SkyBox.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SpawnSphere.png create mode 100644 Templates/BaseGame/game/tools/classIcons/SpotLight.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Sun.png create mode 100644 Templates/BaseGame/game/tools/classIcons/TSForestItemData.png create mode 100644 Templates/BaseGame/game/tools/classIcons/TSStatic.png create mode 100644 Templates/BaseGame/game/tools/classIcons/TerrainBlock.png create mode 100644 Templates/BaseGame/game/tools/classIcons/TimeOfDay.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Trigger.png create mode 100644 Templates/BaseGame/game/tools/classIcons/VolumetricFog.png create mode 100644 Templates/BaseGame/game/tools/classIcons/WaterBlock.png create mode 100644 Templates/BaseGame/game/tools/classIcons/WaterPlane.png create mode 100644 Templates/BaseGame/game/tools/classIcons/Zone.png create mode 100644 Templates/BaseGame/game/tools/classIcons/cameraSpawn.png create mode 100644 Templates/BaseGame/game/tools/classIcons/decal.png create mode 100644 Templates/BaseGame/game/tools/classIcons/decalNode.png create mode 100644 Templates/BaseGame/game/tools/classIcons/default.png create mode 100644 Templates/BaseGame/game/tools/classIcons/fxFoliageReplicator.png create mode 100644 Templates/BaseGame/game/tools/classIcons/fxShapeReplicator.png create mode 100644 Templates/BaseGame/game/tools/classIcons/interiorInstance.png create mode 100644 Templates/BaseGame/game/tools/classIcons/particleEffecterObject.png create mode 100644 Templates/BaseGame/game/tools/classIcons/particleEmitterObject.png create mode 100644 Templates/BaseGame/game/tools/classIcons/particleSimulation.png create mode 100644 Templates/BaseGame/game/tools/classIcons/pathMarker.png create mode 100644 Templates/BaseGame/game/tools/classIcons/volumeLight.png create mode 100644 Templates/BaseGame/game/tools/componentEditor/gui/superToolTipDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/componentEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/componentEditor/scripts/superToolTipDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/convexEditor/convexEditor.cs create mode 100644 Templates/BaseGame/game/tools/convexEditor/convexEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/convexEditor/convexEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/convexEditor/convexEditorSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/convexEditor/convexEditorToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/512_orange.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/convex-editor-btn_d.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/convex-editor-btn_h.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/convex-editor-btn_n.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/materials.cs create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_d.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_h.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_i.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_n.png create mode 100644 Templates/BaseGame/game/tools/convexEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/datablockEditor/DatablockEditorCreatePrompt.ed.gui create mode 100644 Templates/BaseGame/game/tools/datablockEditor/DatablockEditorInspectorWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/datablockEditor/DatablockEditorTreeWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/datablockEditor/datablockEditor.cs create mode 100644 Templates/BaseGame/game/tools/datablockEditor/datablockEditorUndo.cs create mode 100644 Templates/BaseGame/game/tools/datablockEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/debugger/gui/breakConditionDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/gui/connectDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/gui/debugger.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/gui/editWatchDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/gui/findDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/gui/watchDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/debugger/main.cs create mode 100644 Templates/BaseGame/game/tools/debugger/scripts/debugger.ed.cs create mode 100644 Templates/BaseGame/game/tools/decalEditor/add-decal_d.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/add-decal_h.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/add-decal_n.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/decal-editor_d.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/decal-editor_h.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/decal-editor_n.png create mode 100644 Templates/BaseGame/game/tools/decalEditor/decalEditor.cs create mode 100644 Templates/BaseGame/game/tools/decalEditor/decalEditorActions.cs create mode 100644 Templates/BaseGame/game/tools/decalEditor/decalEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/decalEditor/decalEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/decalEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/button.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/button_left.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/button_middle.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/button_right.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/button_toolbar.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/dropDown.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/form.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/formMenu.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconAccept.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconCancel.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconInformation.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconNext.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconPrevious.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconRSSNews.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/iconSave.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/panel_button.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/panel_dark.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/panel_light.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/panel_medium.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_dark.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_plusminus_header.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_plusminus_transparent.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_thin.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_thin_light.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/scroll.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/slider.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/background.jpg create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/create.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_d.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_h.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_i.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/import.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/import_d.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/import_h.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/import_i.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/navPanel.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/open.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/open_d.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/open_h.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/open_i.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/splash.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/topBarLeft.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/topBarMiddle.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/start/topBarRight.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/tabBook.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/textEdit.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/toolWindow.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/toolbar.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/treeView.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/images/window.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/editor-menubar.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/icon-dropdownbar.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-dark.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-list.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-noheader.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout_inner.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/menu-fullborder.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/menubar.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanelProfiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_blue.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_green.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_red.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_white.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_yellow.png create mode 100644 Templates/BaseGame/game/tools/editorClasses/main.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/RSSNews/RSSFeedScript.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/RSSNews/RSSStructs.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/contextPopup.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/core/zip/zipFile.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/expandos.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/fileLoader.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnail.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnailPopup.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormClass.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormContentManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLayoutManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLibraryManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormMessageManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/guiFormReferenceManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/input/applicationEvents.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/input/dragDropEvents.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/input/inputEvents.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/platform/.gitignore create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/preferencesManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectEvents.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectInternalInterface.ed.cs create mode 100644 Templates/BaseGame/game/tools/editorClasses/scripts/utility.ed.cs create mode 100644 Templates/BaseGame/game/tools/forestEditor/brushes.cs create mode 100644 Templates/BaseGame/game/tools/forestEditor/forestEditToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/forestEditor/forestEditor.cs create mode 100644 Templates/BaseGame/game/tools/forestEditor/forestEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/forestEditor/forestEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-brush_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-brush_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-brush_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-element_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-element_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-element_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-mesh_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-mesh_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/new-mesh_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_d.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_h.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_n.png create mode 100644 Templates/BaseGame/game/tools/forestEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/forestEditor/tools.cs create mode 100644 Templates/BaseGame/game/tools/gui/EditorLoadingGui.gui create mode 100644 Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/colladaImport.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/colorPicker.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/cursors.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/fileDialogBase.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/guiDialogs.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconAnimation.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconExistingMaterial.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconIgnoreNode.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconLight.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMaterial.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMesh.png create mode 100644 Templates/BaseGame/game/tools/gui/images/ColladaImport/iconNode.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/NESW.png create mode 100644 Templates/BaseGame/game/tools/gui/images/NWSE.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/arrowbtn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/arrowbtn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_-x.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_-y.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_-z.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_x.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_y.png create mode 100644 Templates/BaseGame/game/tools/gui/images/axis-icon_z.png create mode 100644 Templates/BaseGame/game/tools/gui/images/button.png create mode 100644 Templates/BaseGame/game/tools/gui/images/camera-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/camera-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/camera-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/checkbox-list.png create mode 100644 Templates/BaseGame/game/tools/gui/images/checkbox-list_fliped.png create mode 100644 Templates/BaseGame/game/tools/gui/images/checkbox-menubar.png create mode 100644 Templates/BaseGame/game/tools/gui/images/checkbox.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-icon_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-icon_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/clear-icon_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/collapse-toolbar_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/collapse-toolbar_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/collapse-toolbar_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/copy-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/copy-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/copy-btn_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/copy-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/crosshair.png create mode 100644 Templates/BaseGame/game/tools/gui/images/crosshair_blue.png create mode 100644 Templates/BaseGame/game/tools/gui/images/delete_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/delete_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/delete_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropDown-tab.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropDown.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropdown-button-arrow.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropdown-textEdit.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropslider_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropslider_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/dropslider_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/expand-toolbar_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/expand-toolbar_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/expand-toolbar_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/folder.png create mode 100644 Templates/BaseGame/game/tools/gui/images/folderUp.png create mode 100644 Templates/BaseGame/game/tools/gui/images/folderUp_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/folderUp_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/group-border.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconAccept.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconAdd.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconCancel.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconCollada.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconDelete.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconIcon.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconInformation.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconList.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconLocked.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconNew.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconOpen.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconRefresh.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconSave.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconUnlocked.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconVisible.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconbutton.png create mode 100644 Templates/BaseGame/game/tools/gui/images/iconbuttonsmall.png create mode 100644 Templates/BaseGame/game/tools/gui/images/inactive-overlay.png create mode 100644 Templates/BaseGame/game/tools/gui/images/layers-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/layers-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/layers-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/leftRight.png create mode 100644 Templates/BaseGame/game/tools/gui/images/lock_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/lock_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/lock_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/arrow_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/arrow_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/arrow_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-center_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-center_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-center_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-transform_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-transform_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/object-transform_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/rotate_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/rotate_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/rotate_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/scale_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/scale_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/scale_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-grid_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-grid_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-grid_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-preview_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-preview_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/show-preview_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/translate_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/translate_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/translate_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/world-transform_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/world-transform_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/menubar/world-transform_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/move.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new-folder-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new-folder-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new-folder-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/new_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/open-file_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/open-file_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/open-file_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/radioButton.png create mode 100644 Templates/BaseGame/game/tools/gui/images/reset-icon_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/reset-icon_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/reset-icon_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/retarget-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/retarget-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/retarget-btn_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/retarget-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/rl-loadingbar.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-all_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-all_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-all_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-all_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-as_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-as_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-as_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-as_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-icon_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-icon_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-icon_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/save-icon_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/scrollBar.png create mode 100644 Templates/BaseGame/game/tools/gui/images/separator-h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/separator-v.png create mode 100644 Templates/BaseGame/game/tools/gui/images/slider-w-box.png create mode 100644 Templates/BaseGame/game/tools/gui/images/slider.png create mode 100644 Templates/BaseGame/game/tools/gui/images/tab-border.png create mode 100644 Templates/BaseGame/game/tools/gui/images/tab.png create mode 100644 Templates/BaseGame/game/tools/gui/images/textEdit.png create mode 100644 Templates/BaseGame/game/tools/gui/images/textEditFrame.png create mode 100644 Templates/BaseGame/game/tools/gui/images/textEditSliderBox.png create mode 100644 Templates/BaseGame/game/tools/gui/images/thumbHightlightButton.png create mode 100644 Templates/BaseGame/game/tools/gui/images/toolbar-window.png create mode 100644 Templates/BaseGame/game/tools/gui/images/transp_grid.png create mode 100644 Templates/BaseGame/game/tools/gui/images/treeView.png create mode 100644 Templates/BaseGame/game/tools/gui/images/treeview/default.png create mode 100644 Templates/BaseGame/game/tools/gui/images/treeview/hidden.png create mode 100644 Templates/BaseGame/game/tools/gui/images/upDown.png create mode 100644 Templates/BaseGame/game/tools/gui/images/uv-editor-btn_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/uv-editor-btn_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/uv-editor-btn_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/visible_d.png create mode 100644 Templates/BaseGame/game/tools/gui/images/visible_h.png create mode 100644 Templates/BaseGame/game/tools/gui/images/visible_i.png create mode 100644 Templates/BaseGame/game/tools/gui/images/visible_n.png create mode 100644 Templates/BaseGame/game/tools/gui/images/window.png create mode 100644 Templates/BaseGame/game/tools/gui/materialSelector.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBox.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOK.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKBuy.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancel.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancelDetailsDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNo.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNoCancel.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/messageBoxes/messagePopup.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/objectSelection.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/openFileDialog.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/profilerGraph.cs create mode 100644 Templates/BaseGame/game/tools/gui/profilerGraph.gui create mode 100644 Templates/BaseGame/game/tools/gui/profiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/saveChangesMBDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/saveFileDialog.ed.cs create mode 100644 Templates/BaseGame/game/tools/gui/scriptEditorDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/simViewDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/gui/uvEditor.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/EditorChooseGUI.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/gridTiny2.PNG create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/guiEditorNewGuiDialog.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/guiEditorPrefsDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/gui/guiEditorSelectDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/guiEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/EditorChooseGUI.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/fileDialogs.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorCanvas.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorContentList.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorGroup.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorInspector.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorNewGuiDialog.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorPrefsDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorProfiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorSelectDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorStatusBar.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorToolbox.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorTreeView.ed.cs create mode 100644 Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorUndo.ed.cs create mode 100644 Templates/BaseGame/game/tools/levels/BlankRoom.mis create mode 100644 Templates/BaseGame/game/tools/levels/BlankRoom_preview.png create mode 100644 Templates/BaseGame/game/tools/main.cs create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/MaterialToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/Profiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_cubePreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_previewMat.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_xNeg.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_xPos.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_yNeg.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_yPos.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_zNeg.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cube_zPos.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_i.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemapEd_spherePreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cubepreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cylinderpreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_spherepreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubematEd_cylinderPreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cubepreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/cylinderpreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/gridTiny2.PNG create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPreviewWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPropertiesWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_cubePreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_d.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_h.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_n.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderPreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_mappedMat.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_pyramidPreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_d.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_h.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_n.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_spherePreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusKnotPreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusPreview.max create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/new-material_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/new-material_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/new-material_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/pyramidpreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/screenFaded.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/scrollBox.jpg create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/spherepreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/torusknotpreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/torusknowpreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/toruspreview.dts create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/unknownImage.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/unsavedWarn.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-none_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-none_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-none_i.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-none_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_i.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-square_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-square_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-square_i.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-square_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_d.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_h.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_i.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_n.png create mode 100644 Templates/BaseGame/game/tools/materialEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/materialEditor/scripts/materialEditorUndo.ed.cs create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditor.cs create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorSettingsTab.gui create mode 100644 Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorToolbar.gui create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/images/DefaultHandle.png create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_d.png create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_h.png create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_n.png create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.cs create mode 100644 Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/CreateNewNavMeshDlg.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/NavEditorConsoleDlg.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/NavEditorSettingsTab.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui create mode 100644 Templates/BaseGame/game/tools/navEditor/done.wav create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-cover_d.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-cover_h.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-cover_n.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-editor_d.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-editor_h.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-editor_n.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-link_d.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-link_h.png create mode 100644 Templates/BaseGame/game/tools/navEditor/images/nav-link_n.png create mode 100644 Templates/BaseGame/game/tools/navEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/navEditor/navEditor.cs create mode 100644 Templates/BaseGame/game/tools/particleEditor/ParticleEditor.ed.gui create mode 100644 Templates/BaseGame/game/tools/particleEditor/images/play_btn_d.png create mode 100644 Templates/BaseGame/game/tools/particleEditor/images/play_btn_h.png create mode 100644 Templates/BaseGame/game/tools/particleEditor/images/play_btn_n.png create mode 100644 Templates/BaseGame/game/tools/particleEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/particleEditor/particleEditorUndo.ed.cs create mode 100644 Templates/BaseGame/game/tools/particleEditor/particleEmitterEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/particleEditor/particleParticleEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/physicsTools/main.cs create mode 100644 Templates/BaseGame/game/tools/resources/.gitignore create mode 100644 Templates/BaseGame/game/tools/riverEditor/RiverEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/riverEditor/RiverEditorSettingsTab.gui create mode 100644 Templates/BaseGame/game/tools/riverEditor/RiverEditorToolbar.gui create mode 100644 Templates/BaseGame/game/tools/riverEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/riverEditor/riverEditor.cs create mode 100644 Templates/BaseGame/game/tools/riverEditor/riverEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/roadEditor/RoadEditorGui.gui create mode 100644 Templates/BaseGame/game/tools/roadEditor/RoadEditorSettingsTab.gui create mode 100644 Templates/BaseGame/game/tools/roadEditor/RoadEditorToolbar.gui create mode 100644 Templates/BaseGame/game/tools/roadEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/roadEditor/roadEditor.cs create mode 100644 Templates/BaseGame/game/tools/roadEditor/roadEditorGui.cs create mode 100644 Templates/BaseGame/game/tools/settings.xml create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/Profiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorSettingsTab.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAdvancedWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAnimWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPreviewWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPropWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/back_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/back_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/back_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/highlight_material.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_d.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_h.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_n.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/transition_slider.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/images/trigger_marker.png create mode 100644 Templates/BaseGame/game/tools/shapeEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs create mode 100644 Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorHints.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/AddFMODProjectDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/AxisGizmoSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/CameraSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/EditorChooseLevelGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/EditorSettingsWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/GeneralSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/GenericPromptDialog.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ManageBookmarksWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ManageSFXParametersWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ObjectEditorSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ObjectSnapOptionsWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/SelectObjectsWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorSettingsTab.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorVSettingsGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TimeAdjustGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ConvexEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/DecalEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ForestEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/MeshRoadEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/NavEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RiverEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RoadEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ShapeEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainEditPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainPainterPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/WorldEditorPalette.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/init.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/TransformSelectionWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/VisibilityLayerWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorInspectorWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiCreateNewTerrainGui.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainEditorToolbar.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainExportGui.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainImportGui.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorCreatorWindow.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorMissionInspector.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui create mode 100644 Templates/BaseGame/game/tools/worldEditor/gui/profiles.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_3darrow.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagleft.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagright.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_3dleftright.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_3dupdown.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_grab.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_hand.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/CUR_rotate.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/DefaultHandle.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/LockedHandle.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/SelectHandle.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/boxBrush_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/boxBrush_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/boxBrush_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/circleBrush_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/circleBrush_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/circleBrush_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/maskBrush_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/maskBrush_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/maskBrush_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setEmpty_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setEmpty_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setEmpty_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/setHeight_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/softCurve_d.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/softCurve_h.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/softCurve_n.PNG create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/new_layer_icon.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-large.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_n.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/world.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_d.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_h.png create mode 100644 Templates/BaseGame/game/tools/worldEditor/main.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/AddFMODProjectDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/EditorChooseLevelGui.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/ManageSFXParametersWindow.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/SelectObjectsWindow.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/cameraBookmarks.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/cameraCommands.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editor.bind.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editor.keybinds.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editorInputCommands.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editorPlugin.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editorRender.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editorSettingsWindow.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editors/missionArea.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/editors/worldEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/levelInfoEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/simObjectEditor.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/lighting.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/objectSnapOptions.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/transformSelection.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/undoManager.ed.cs create mode 100644 Templates/BaseGame/game/tools/worldEditor/scripts/visibilityLayer.ed.cs create mode 100644 Templates/BaseGame/source/readme.txt create mode 100644 Templates/BaseGame/source/torqueConfig.h create mode 100644 Templates/BaseGame/thumb.png create mode 100644 Templates/Full/game/Full.torsion.opt diff --git a/Engine/source/forest/glsl/windDeformationGLSL.cpp b/Engine/source/forest/glsl/windDeformationGLSL.cpp index ae32b51f7..9c9ed6f42 100644 --- a/Engine/source/forest/glsl/windDeformationGLSL.cpp +++ b/Engine/source/forest/glsl/windDeformationGLSL.cpp @@ -60,7 +60,7 @@ MODULE_END; WindDeformationGLSL::WindDeformationGLSL() - : mDep( "shaders/common/gl/wind.glsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/wind.glsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/forest/hlsl/windDeformationHLSL.cpp b/Engine/source/forest/hlsl/windDeformationHLSL.cpp index 24acf769a..08bce353e 100644 --- a/Engine/source/forest/hlsl/windDeformationHLSL.cpp +++ b/Engine/source/forest/hlsl/windDeformationHLSL.cpp @@ -60,7 +60,7 @@ MODULE_END; WindDeformationHLSL::WindDeformationHLSL() - : mDep( "shaders/common/wind.hlsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/wind.hlsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp index fd2e2e58b..16440eb64 100644 --- a/Engine/source/gfx/D3D11/gfxD3D11Device.cpp +++ b/Engine/source/gfx/D3D11/gfxD3D11Device.cpp @@ -670,8 +670,8 @@ void GFXD3D11Device::setupGenericShaders(GenericShaderType type) //shader model 4.0 is enough for the generic shaders const char* shaderModel = "4.0"; shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/colorV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/colorP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorP.hlsl")); shaderData->setField("pixVersion", shaderModel); shaderData->registerObject(); mGenericShader[GSColor] = shaderData->getShader(); @@ -680,8 +680,8 @@ void GFXD3D11Device::setupGenericShaders(GenericShaderType type) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/modColorTextureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/modColorTextureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureP.hlsl")); shaderData->setField("pixVersion", shaderModel); shaderData->registerObject(); mGenericShader[GSModColorTexture] = shaderData->getShader(); @@ -690,8 +690,8 @@ void GFXD3D11Device::setupGenericShaders(GenericShaderType type) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/addColorTextureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/addColorTextureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureP.hlsl")); shaderData->setField("pixVersion", shaderModel); shaderData->registerObject(); mGenericShader[GSAddColorTexture] = shaderData->getShader(); @@ -700,8 +700,8 @@ void GFXD3D11Device::setupGenericShaders(GenericShaderType type) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/textureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/textureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureP.hlsl")); shaderData->setField("pixVersion", shaderModel); shaderData->registerObject(); mGenericShader[GSTexture] = shaderData->getShader(); diff --git a/Engine/source/gfx/D3D9/gfxD3D9Device.cpp b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp index 4f23d7a7d..05c3474dc 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9Device.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp @@ -154,8 +154,8 @@ inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSCo ShaderData *shaderData; shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/colorV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/colorP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/colorP.hlsl")); shaderData->setField("pixVersion", "3.0"); shaderData->registerObject(); mGenericShader[GSColor] = shaderData->getShader(); @@ -164,8 +164,8 @@ inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSCo Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/modColorTextureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/modColorTextureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/modColorTextureP.hlsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "3.0"); shaderData->registerObject(); @@ -175,8 +175,8 @@ inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSCo Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/addColorTextureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/addColorTextureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/addColorTextureP.hlsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "3.0"); shaderData->registerObject(); @@ -186,8 +186,8 @@ inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSCo Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("DXVertexShaderFile", "shaders/common/fixedFunction/textureV.hlsl"); - shaderData->setField("DXPixelShaderFile", "shaders/common/fixedFunction/textureP.hlsl"); + shaderData->setField("DXVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureV.hlsl")); + shaderData->setField("DXPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/textureP.hlsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "3.0"); shaderData->registerObject(); diff --git a/Engine/source/gfx/gfxTextureManager.cpp b/Engine/source/gfx/gfxTextureManager.cpp index 781e0daa2..e9a3e57d4 100644 --- a/Engine/source/gfx/gfxTextureManager.cpp +++ b/Engine/source/gfx/gfxTextureManager.cpp @@ -42,9 +42,9 @@ using namespace Torque; S32 GFXTextureManager::smTextureReductionLevel = 0; -String GFXTextureManager::smMissingTexturePath("core/art/missingTexture"); -String GFXTextureManager::smUnavailableTexturePath("core/art/unavailable"); -String GFXTextureManager::smWarningTexturePath("core/art/warnmat"); +String GFXTextureManager::smMissingTexturePath(Con::getVariable("$Core::MissingTexturePath")); +String GFXTextureManager::smUnavailableTexturePath(Con::getVariable("$Core::UnAvailableTexturePath")); +String GFXTextureManager::smWarningTexturePath(Con::getVariable("$Core::WarningTexturePath")); GFXTextureManager::EventSignal GFXTextureManager::smEventSignal; diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp index ce22f16a1..bc348671b 100644 --- a/Engine/source/gfx/gl/gfxGLDevice.cpp +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -780,8 +780,8 @@ void GFXGLDevice::setupGenericShaders( GenericShaderType type ) ShaderData *shaderData; shaderData = new ShaderData(); - shaderData->setField("OGLVertexShaderFile", "shaders/common/fixedFunction/gl/colorV.glsl"); - shaderData->setField("OGLPixelShaderFile", "shaders/common/fixedFunction/gl/colorP.glsl"); + shaderData->setField("OGLVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/colorV.glsl")); + shaderData->setField("OGLPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/colorP.glsl")); shaderData->setField("pixVersion", "2.0"); shaderData->registerObject(); mGenericShader[GSColor] = shaderData->getShader(); @@ -790,8 +790,8 @@ void GFXGLDevice::setupGenericShaders( GenericShaderType type ) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("OGLVertexShaderFile", "shaders/common/fixedFunction/gl/modColorTextureV.glsl"); - shaderData->setField("OGLPixelShaderFile", "shaders/common/fixedFunction/gl/modColorTextureP.glsl"); + shaderData->setField("OGLVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/modColorTextureV.glsl")); + shaderData->setField("OGLPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/modColorTextureP.glsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "2.0"); shaderData->registerObject(); @@ -801,8 +801,8 @@ void GFXGLDevice::setupGenericShaders( GenericShaderType type ) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("OGLVertexShaderFile", "shaders/common/fixedFunction/gl/addColorTextureV.glsl"); - shaderData->setField("OGLPixelShaderFile", "shaders/common/fixedFunction/gl/addColorTextureP.glsl"); + shaderData->setField("OGLVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/addColorTextureV.glsl")); + shaderData->setField("OGLPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/addColorTextureP.glsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "2.0"); shaderData->registerObject(); @@ -812,8 +812,8 @@ void GFXGLDevice::setupGenericShaders( GenericShaderType type ) Sim::getRootGroup()->addObject(shaderData); shaderData = new ShaderData(); - shaderData->setField("OGLVertexShaderFile", "shaders/common/fixedFunction/gl/textureV.glsl"); - shaderData->setField("OGLPixelShaderFile", "shaders/common/fixedFunction/gl/textureP.glsl"); + shaderData->setField("OGLVertexShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/textureV.glsl")); + shaderData->setField("OGLPixelShaderFile", String(Con::getVariable("$Core::CommonShaderPath")) + String("/fixedFunction/gl/textureP.glsl")); shaderData->setSamplerName("$diffuseMap", 0); shaderData->setField("pixVersion", "2.0"); shaderData->registerObject(); diff --git a/Engine/source/materials/shaderData.cpp b/Engine/source/materials/shaderData.cpp index 829bbbbb1..29c379ed7 100644 --- a/Engine/source/materials/shaderData.cpp +++ b/Engine/source/materials/shaderData.cpp @@ -48,10 +48,10 @@ ConsoleDocClass( ShaderData, "// Used for the procedural clould system\n" "singleton ShaderData( CloudLayerShader )\n" "{\n" - " DXVertexShaderFile = \"shaders/common/cloudLayerV.hlsl\";\n" - " DXPixelShaderFile = \"shaders/common/cloudLayerP.hlsl\";\n" - " OGLVertexShaderFile = \"shaders/common/gl/cloudLayerV.glsl\";\n" - " OGLPixelShaderFile = \"shaders/common/gl/cloudLayerP.glsl\";\n" + " DXVertexShaderFile = $Core::CommonShaderPath @ \"/cloudLayerV.hlsl\";\n" + " DXPixelShaderFile = $Core::CommonShaderPath @ \"/cloudLayerP.hlsl\";\n" + " OGLVertexShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerV.glsl\";\n" + " OGLPixelShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerP.glsl\";\n" " pixVersion = 2.0;\n" "};\n" "@endtsexample\n\n" @@ -109,8 +109,8 @@ void ShaderData::initPersistFields() "@tsexample\n" "singleton ShaderData( FlashShader )\n" "{\n" - "DXVertexShaderFile = \"shaders/common/postFx/flashV.hlsl\";\n" - "DXPixelShaderFile = \"shaders/common/postFx/flashP.hlsl\";\n\n" + "DXVertexShaderFile = $shaderGen::cachePath @ \"/postFx/flashV.hlsl\";\n" + "DXPixelShaderFile = $shaderGen::cachePath @ \"/postFx/flashP.hlsl\";\n\n" " //Define setting the color of WHITE_COLOR.\n" "defines = \"WHITE_COLOR=float4(1.0,1.0,1.0,0.0)\";\n\n" "pixVersion = 2.0\n" diff --git a/Engine/source/postFx/postEffect.cpp b/Engine/source/postFx/postEffect.cpp index 7e4a6fed8..65cb69d20 100644 --- a/Engine/source/postFx/postEffect.cpp +++ b/Engine/source/postFx/postEffect.cpp @@ -411,10 +411,6 @@ bool PostEffect::onAdd() texFilename[0] == '#' ) continue; - // If '/', then path is specified, open normally - if ( texFilename[0] != '/' ) - texFilename = scriptPath.getFullPath() + '/' + texFilename; - // Try to load the texture. bool success = mTextures[i].set( texFilename, &PostFxTextureProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); if (!success) diff --git a/Engine/source/shaderGen/GLSL/bumpGLSL.cpp b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp index bc11a156f..020107b57 100644 --- a/Engine/source/shaderGen/GLSL/bumpGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp @@ -236,7 +236,7 @@ void BumpFeatGLSL::setTexData( Material::StageData &stageDat, ParallaxFeatGLSL::ParallaxFeatGLSL() - : mIncludeDep( "shaders/common/gl/torque.glsl" ) + : mIncludeDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )) { addDependency( &mIncludeDep ); } diff --git a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp index 2ecb56df6..192641775 100644 --- a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp @@ -30,7 +30,7 @@ PixelSpecularGLSL::PixelSpecularGLSL() - : mDep( "shaders/common/gl/lighting.glsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/lighting.glsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp index cabedc14c..0a7ec2159 100644 --- a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp @@ -830,7 +830,7 @@ Var* ShaderFeatureGLSL::addOutDetailTexCoord( Vector &compon //**************************************************************************** DiffuseMapFeatGLSL::DiffuseMapFeatGLSL() -: mTorqueDep("shaders/common/gl/torque.glsl") +: mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl")) { addDependency(&mTorqueDep); } @@ -1975,7 +1975,7 @@ void ReflectCubeFeatGLSL::setTexData( Material::StageData &stageDat, //**************************************************************************** RTLightingFeatGLSL::RTLightingFeatGLSL() - : mDep( "shaders/common/gl/lighting.glsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/lighting.glsl" )) { addDependency( &mDep ); } @@ -2190,7 +2190,7 @@ ShaderFeature::Resources RTLightingFeatGLSL::getResources( const MaterialFeature //**************************************************************************** FogFeatGLSL::FogFeatGLSL() - : mFogDep( "shaders/common/gl/torque.glsl" ) + : mFogDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )) { addDependency( &mFogDep ); } @@ -2321,7 +2321,7 @@ ShaderFeature::Resources FogFeatGLSL::getResources( const MaterialFeatureData &f //**************************************************************************** VisibilityFeatGLSL::VisibilityFeatGLSL() - : mTorqueDep( "shaders/common/gl/torque.glsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )) { addDependency( &mTorqueDep ); } @@ -2487,7 +2487,7 @@ void RenderTargetZeroGLSL::processPix( Vector &componentList, //**************************************************************************** HDROutGLSL::HDROutGLSL() - : mTorqueDep( "shaders/common/gl/torque.glsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )) { addDependency( &mTorqueDep ); } @@ -2508,7 +2508,7 @@ void HDROutGLSL::processPix( Vector &componentList, #include "T3D/fx/groundCover.h" FoliageFeatureGLSL::FoliageFeatureGLSL() -: mDep( "shaders/common/gl/foliage.glsl" ) +: mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/foliage.glsl" )) { addDependency( &mDep ); } @@ -2654,7 +2654,7 @@ void ParticleNormalFeatureGLSL::processVert(Vector &componentL //**************************************************************************** ImposterVertFeatureGLSL::ImposterVertFeatureGLSL() - : mDep( "shaders/common/gl/imposter.glsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/imposter.glsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp index 6e068c7cf..d5556cb1f 100644 --- a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp @@ -37,8 +37,8 @@ void ShaderGenPrinterGLSL::printShaderHeader( Stream& stream ) stream.write( dStrlen(header1), header1 ); // Cheap HLSL compatibility. - const char* header3 = "#include \"shaders/common/gl/hlslCompat.glsl\"\r\n"; - stream.write( dStrlen(header3), header3 ); + String header3 = String("#include \"") + String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/hlslCompat.glsl\"\r\n"); + stream.write(dStrlen(header3), header3.c_str()); const char* header4 = "\r\n"; stream.write( dStrlen(header4), header4 ); diff --git a/Engine/source/shaderGen/HLSL/bumpHLSL.cpp b/Engine/source/shaderGen/HLSL/bumpHLSL.cpp index f6beb4cfc..79ea27714 100644 --- a/Engine/source/shaderGen/HLSL/bumpHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/bumpHLSL.cpp @@ -268,7 +268,7 @@ void BumpFeatHLSL::setTexData( Material::StageData &stageDat, ParallaxFeatHLSL::ParallaxFeatHLSL() - : mIncludeDep( "shaders/common/torque.hlsl" ) + : mIncludeDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )) { addDependency( &mIncludeDep ); } diff --git a/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp index b5a5a98b1..3a794a394 100644 --- a/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp @@ -30,7 +30,7 @@ PixelSpecularHLSL::PixelSpecularHLSL() - : mDep( "shaders/common/lighting.hlsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/lighting.hlsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp index 6e8a08347..8690ae610 100644 --- a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp @@ -853,7 +853,7 @@ Var* ShaderFeatureHLSL::addOutDetailTexCoord( Vector &compon //**************************************************************************** DiffuseMapFeatHLSL::DiffuseMapFeatHLSL() -: mTorqueDep("shaders/common/torque.hlsl") +: mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl")) { addDependency(&mTorqueDep); } @@ -2168,7 +2168,7 @@ void ReflectCubeFeatHLSL::setTexData( Material::StageData &stageDat, //**************************************************************************** RTLightingFeatHLSL::RTLightingFeatHLSL() - : mDep( "shaders/common/lighting.hlsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/lighting.hlsl" )) { addDependency( &mDep ); } @@ -2383,7 +2383,7 @@ ShaderFeature::Resources RTLightingFeatHLSL::getResources( const MaterialFeature //**************************************************************************** FogFeatHLSL::FogFeatHLSL() - : mFogDep( "shaders/common/torque.hlsl" ) + : mFogDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )) { addDependency( &mFogDep ); } @@ -2514,7 +2514,7 @@ ShaderFeature::Resources FogFeatHLSL::getResources( const MaterialFeatureData &f //**************************************************************************** VisibilityFeatHLSL::VisibilityFeatHLSL() - : mTorqueDep( "shaders/common/torque.hlsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )) { addDependency( &mTorqueDep ); } @@ -2681,7 +2681,7 @@ void RenderTargetZeroHLSL::processPix( Vector &componentList, //**************************************************************************** HDROutHLSL::HDROutHLSL() - : mTorqueDep( "shaders/common/torque.hlsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )) { addDependency( &mTorqueDep ); } @@ -2702,7 +2702,7 @@ void HDROutHLSL::processPix( Vector &componentList, #include "T3D/fx/groundCover.h" FoliageFeatureHLSL::FoliageFeatureHLSL() -: mDep( "shaders/common/foliage.hlsl" ) +: mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/foliage.hlsl" )) { addDependency( &mDep ); } @@ -2848,7 +2848,7 @@ void ParticleNormalFeatureHLSL::processVert(Vector &componentL //**************************************************************************** ImposterVertFeatureHLSL::ImposterVertFeatureHLSL() - : mDep( "shaders/common/imposter.hlsl" ) + : mDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/imposter.hlsl" )) { addDependency( &mDep ); } diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp index f40776b80..193f9587c 100644 --- a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp @@ -70,7 +70,7 @@ MODULE_END; TerrainFeatGLSL::TerrainFeatGLSL() - : mTorqueDep( "shaders/common/gl/torque.glsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )) { addDependency( &mTorqueDep ); } @@ -297,8 +297,8 @@ U32 TerrainBaseMapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) co } TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL() - : mTorqueDep( "shaders/common/gl/torque.glsl" ), - mTerrainDep( "shaders/common/terrain/terrain.glsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )), + mTerrainDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/terrain/terrain.glsl" )) { addDependency( &mTorqueDep ); @@ -667,8 +667,8 @@ U32 TerrainDetailMapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) TerrainMacroMapFeatGLSL::TerrainMacroMapFeatGLSL() - : mTorqueDep( "shaders/common/gl/torque.glsl" ), - mTerrainDep( "shaders/common/terrain/terrain.glsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/gl/torque.glsl" )), + mTerrainDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/terrain/terrain.glsl" )) { addDependency( &mTorqueDep ); diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp index 9bd77b664..74fdd417b 100644 --- a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp @@ -69,7 +69,7 @@ MODULE_END; TerrainFeatHLSL::TerrainFeatHLSL() - : mTorqueDep( "shaders/common/torque.hlsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )) { addDependency( &mTorqueDep ); } @@ -315,8 +315,8 @@ U32 TerrainBaseMapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) co } TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL() - : mTorqueDep( "shaders/common/torque.hlsl" ), - mTerrainDep( "shaders/common/terrain/terrain.hlsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )), + mTerrainDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/terrain/terrain.hlsl" )) { addDependency( &mTorqueDep ); @@ -692,8 +692,8 @@ U32 TerrainDetailMapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) TerrainMacroMapFeatHLSL::TerrainMacroMapFeatHLSL() - : mTorqueDep( "shaders/common/torque.hlsl" ), - mTerrainDep( "shaders/common/terrain/terrain.hlsl" ) + : mTorqueDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/torque.hlsl" )), + mTerrainDep(String(Con::getVariable("$Core::CommonShaderPath")) + String("/terrain/terrain.hlsl" )) { addDependency( &mTorqueDep ); diff --git a/Templates/BaseGame/BaseGame.cmake b/Templates/BaseGame/BaseGame.cmake new file mode 100644 index 000000000..8b0a6700a --- /dev/null +++ b/Templates/BaseGame/BaseGame.cmake @@ -0,0 +1 @@ +# Project-specific Cmake configurations go here \ No newline at end of file diff --git a/Templates/BaseGame/DeleteCachedDTSs.bat b/Templates/BaseGame/DeleteCachedDTSs.bat new file mode 100644 index 000000000..8f0dc258e --- /dev/null +++ b/Templates/BaseGame/DeleteCachedDTSs.bat @@ -0,0 +1 @@ +for /R %%a IN (*.dae) do IF EXIST "%%~pna.cached.dts" del "%%~pna.cached.dts" diff --git a/Templates/BaseGame/DeleteCachedDTSs.command b/Templates/BaseGame/DeleteCachedDTSs.command new file mode 100644 index 000000000..f497316e0 --- /dev/null +++ b/Templates/BaseGame/DeleteCachedDTSs.command @@ -0,0 +1,15 @@ +#!/bin/sh + +cd "`dirname "$0"`" + +for i in $(find . -type f \( -iname "*.dae" \)) +do + len=$((${#i} - 4)) + file=${i:0:$len}.cached.dts + if [ -e $file ] + then + echo "Removing ${file}" + rm $file + fi +done + diff --git a/Templates/BaseGame/DeleteDSOs.bat b/Templates/BaseGame/DeleteDSOs.bat new file mode 100644 index 000000000..42228e4b3 --- /dev/null +++ b/Templates/BaseGame/DeleteDSOs.bat @@ -0,0 +1,6 @@ +for /R %%a IN (*.cs) do IF EXIST "%%a.dso" del "%%a.dso" +for /R %%a IN (*.cs) do IF EXIST "%%a.edso" del "%%a.edso" +for /R %%a IN (*.gui) do IF EXIST "%%a.dso" del "%%a.dso" +for /R %%a IN (*.gui) do IF EXIST "%%a.edso" del "%%a.edso" +for /R %%a IN (*.ts) do IF EXIST "%%a.dso" del "%%a.dso" +for /R %%a IN (*.ts) do IF EXIST "%%a.edso" del "%%a.edso" diff --git a/Templates/BaseGame/DeleteDSOs.command b/Templates/BaseGame/DeleteDSOs.command new file mode 100644 index 000000000..f6e805a31 --- /dev/null +++ b/Templates/BaseGame/DeleteDSOs.command @@ -0,0 +1,19 @@ +#!/bin/sh + +cd "`dirname "$0"`" + +for i in $(find . -type f \( -iname "*.cs" \)) +do + file=${i}.dso + if [ -e $file ] + then + echo "Removing ${file}" + rm $file + fi + file=${i}.edso + if [ -e $file ] + then + echo "Removing ${file}" + rm $file + fi +done diff --git a/Templates/BaseGame/DeletePrefs.bat b/Templates/BaseGame/DeletePrefs.bat new file mode 100644 index 000000000..e61988210 --- /dev/null +++ b/Templates/BaseGame/DeletePrefs.bat @@ -0,0 +1,6 @@ +del /s prefs.cs +del /s config.cs +del /s banlist.cs +del /s config.cs.dso +del /s prefs.cs.dso +del /s banlist.cs.dso diff --git a/Templates/BaseGame/DeletePrefs.command b/Templates/BaseGame/DeletePrefs.command new file mode 100644 index 000000000..43961562f --- /dev/null +++ b/Templates/BaseGame/DeletePrefs.command @@ -0,0 +1,3 @@ +#!/bin/sh + +find "`dirname "$0"`" -type f \( -name "prefs.cs" -or -name "config.cs" -or -name "banlist.cs" -or -name "prefs.cs.dso" -or -name "config.cs.dso" -or -name "banlist.cs.dso" \) -exec rm {} \; diff --git a/Templates/BaseGame/cleanShaders.bat b/Templates/BaseGame/cleanShaders.bat new file mode 100644 index 000000000..047b30d21 --- /dev/null +++ b/Templates/BaseGame/cleanShaders.bat @@ -0,0 +1,7 @@ +REM Delete procedural shaders + +del /q /a:-R game\shaders\procedural\*.* + +REM Delete dumped shader disassembly files + +del /q /s /a:-R *_dis.txt \ No newline at end of file diff --git a/Templates/BaseGame/cleanShaders.command b/Templates/BaseGame/cleanShaders.command new file mode 100644 index 000000000..93cebdcea --- /dev/null +++ b/Templates/BaseGame/cleanShaders.command @@ -0,0 +1,4 @@ +#!/bin/sh + +cd "`dirname "$0"`" +rm -rf game/shaders/procedural/*.* diff --git a/Templates/BaseGame/game/Template.torsion.exports b/Templates/BaseGame/game/Template.torsion.exports new file mode 100644 index 000000000..c6a971eb4 --- /dev/null +++ b/Templates/BaseGame/game/Template.torsion.exports @@ -0,0 +1 @@ + diff --git a/Templates/BaseGame/game/core/audio.cs b/Templates/BaseGame/game/core/audio.cs new file mode 100644 index 000000000..a5932de8f --- /dev/null +++ b/Templates/BaseGame/game/core/audio.cs @@ -0,0 +1,436 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Source groups. +//----------------------------------------------------------------------------- + +singleton SFXDescription( AudioMaster ); +singleton SFXSource( AudioChannelMaster ) +{ + description = AudioMaster; +}; + +singleton SFXDescription( AudioChannel ) +{ + sourceGroup = AudioChannelMaster; +}; + +singleton SFXSource( AudioChannelDefault ) +{ + description = AudioChannel; +}; +singleton SFXSource( AudioChannelGui ) +{ + description = AudioChannel; +}; +singleton SFXSource( AudioChannelEffects ) +{ + description = AudioChannel; +}; +singleton SFXSource( AudioChannelMessages ) +{ + description = AudioChannel; +}; +singleton SFXSource( AudioChannelMusic ) +{ + description = AudioChannel; +}; + +// Set default playback states of the channels. + +AudioChannelMaster.play(); +AudioChannelDefault.play(); + +AudioChannelGui.play(); +AudioChannelMusic.play(); +AudioChannelMessages.play(); + +// Stop in-game effects channels. +AudioChannelEffects.stop(); + +//----------------------------------------------------------------------------- +// Master SFXDescriptions. +//----------------------------------------------------------------------------- + +// Master description for interface audio. +singleton SFXDescription( AudioGui ) +{ + volume = 1.0; + sourceGroup = AudioChannelGui; +}; + +// Master description for game effects audio. +singleton SFXDescription( AudioEffect ) +{ + volume = 1.0; + sourceGroup = AudioChannelEffects; +}; + +// Master description for audio in notifications. +singleton SFXDescription( AudioMessage ) +{ + volume = 1.0; + sourceGroup = AudioChannelMessages; +}; + +// Master description for music. +singleton SFXDescription( AudioMusic ) +{ + volume = 1.0; + sourceGroup = AudioChannelMusic; +}; + +//----------------------------------------------------------------------------- +// SFX Functions. +//----------------------------------------------------------------------------- + +/// This initializes the sound system device from +/// the defaults in the $pref::SFX:: globals. +function sfxStartup() +{ + echo( "\nsfxStartup..." ); + + // If we have a provider set, try initialize a device now. + + if( $pref::SFX::provider !$= "" ) + { + if( sfxInit() ) + return; + else + { + // Force auto-detection. + $pref::SFX::autoDetect = true; + } + } + + // If enabled autodetect a safe device. + + if( ( !isDefined( "$pref::SFX::autoDetect" ) || $pref::SFX::autoDetect ) && + sfxAutodetect() ) + return; + + // Failure. + + error( " Failed to initialize device!\n\n" ); + + $pref::SFX::provider = ""; + $pref::SFX::device = ""; + + return; +} + + +/// This initializes the sound system device from +/// the defaults in the $pref::SFX:: globals. +function sfxInit() +{ + // If already initialized, shut down the current device first. + + if( sfxGetDeviceInfo() !$= "" ) + sfxShutdown(); + + // Start it up! + %maxBuffers = $pref::SFX::useHardware ? -1 : $pref::SFX::maxSoftwareBuffers; + if ( !sfxCreateDevice( $pref::SFX::provider, $pref::SFX::device, $pref::SFX::useHardware, %maxBuffers ) ) + return false; + + // This returns a tab seperated string with + // the initialized system info. + %info = sfxGetDeviceInfo(); + $pref::SFX::provider = getField( %info, 0 ); + $pref::SFX::device = getField( %info, 1 ); + $pref::SFX::useHardware = getField( %info, 2 ); + %useHardware = $pref::SFX::useHardware ? "Yes" : "No"; + %maxBuffers = getField( %info, 3 ); + + echo( " Provider: " @ $pref::SFX::provider ); + echo( " Device: " @ $pref::SFX::device ); + echo( " Hardware: " @ %useHardware ); + echo( " Max Buffers: " @ %maxBuffers ); + echo( " " ); + + if( isDefined( "$pref::SFX::distanceModel" ) ) + sfxSetDistanceModel( $pref::SFX::distanceModel ); + if( isDefined( "$pref::SFX::dopplerFactor" ) ) + sfxSetDopplerFactor( $pref::SFX::dopplerFactor ); + if( isDefined( "$pref::SFX::rolloffFactor" ) ) + sfxSetRolloffFactor( $pref::SFX::rolloffFactor ); + + // Restore master volume. + + sfxSetMasterVolume( $pref::SFX::masterVolume ); + + // Restore channel volumes. + + for( %channel = 0; %channel <= 8; %channel ++ ) + sfxSetChannelVolume( %channel, $pref::SFX::channelVolume[ %channel ] ); + + return true; +} + + +/// Destroys the current sound system device. +function sfxShutdown() +{ + // Store volume prefs. + + $pref::SFX::masterVolume = sfxGetMasterVolume(); + + for( %channel = 0; %channel <= 8; %channel ++ ) + $pref::SFX::channelVolume[ %channel ] = sfxGetChannelVolume( %channel ); + + // We're assuming here that a null info + // string means that no device is loaded. + if( sfxGetDeviceInfo() $= "" ) + return; + + sfxDeleteDevice(); +} + + +/// Determines which of the two SFX providers is preferable. +function sfxCompareProvider( %providerA, %providerB ) +{ + if( %providerA $= %providerB ) + return 0; + + switch$( %providerA ) + { + // Always prefer FMOD over anything else. + case "FMOD": + return 1; + + // Prefer OpenAL over anything but FMOD. + case "OpenAL": + if( %providerB $= "FMOD" ) + return -1; + else + return 1; + + // choose XAudio over DirectSound + case "XAudio": + if( %providerB $= "FMOD" || %providerB $= "OpenAL" ) + return -1; + else + return 0; + + case "DirectSound": + if( %providerB !$= "FMOD" && %providerB !$= "OpenAL" && %providerB !$= "XAudio" ) + return 1; + else + return -1; + + default: + return -1; + } +} + + +/// Try to detect and initalize the best SFX device available. +function sfxAutodetect() +{ + // Get all the available devices. + + %devices = sfxGetAvailableDevices(); + + // Collect and sort the devices by preferentiality. + + %deviceTrySequence = new ArrayObject(); + %bestMatch = -1; + %count = getRecordCount( %devices ); + for( %i = 0; %i < %count; %i ++ ) + { + %info = getRecord( %devices, %i ); + %provider = getField( %info, 0 ); + + %deviceTrySequence.push_back( %provider, %info ); + } + + %deviceTrySequence.sortfkd( "sfxCompareProvider" ); + + // Try the devices in order. + + %count = %deviceTrySequence.count(); + for( %i = 0; %i < %count; %i ++ ) + { + %provider = %deviceTrySequence.getKey( %i ); + %info = %deviceTrySequence.getValue( %i ); + + $pref::SFX::provider = %provider; + $pref::SFX::device = getField( %info, 1 ); + $pref::SFX::useHardware = getField( %info, 2 ); + + // By default we've decided to avoid hardware devices as + // they are buggy and prone to problems. + $pref::SFX::useHardware = false; + + if( sfxInit() ) + { + $pref::SFX::autoDetect = false; + %deviceTrySequence.delete(); + return true; + } + } + + // Found no suitable device. + + error( "sfxAutodetect - Could not initialize a valid SFX device." ); + + $pref::SFX::provider = ""; + $pref::SFX::device = ""; + $pref::SFX::useHardware = ""; + + %deviceTrySequence.delete(); + + return false; +} + + +//----------------------------------------------------------------------------- +// Backwards-compatibility with old channel system. +//----------------------------------------------------------------------------- + +// Volume channel IDs for backwards-compatibility. + +$GuiAudioType = 1; // Interface. +$SimAudioType = 2; // Game. +$MessageAudioType = 3; // Notifications. +$MusicAudioType = 4; // Music. + +$AudioChannels[ 0 ] = AudioChannelDefault; +$AudioChannels[ $GuiAudioType ] = AudioChannelGui; +$AudioChannels[ $SimAudioType ] = AudioChannelEffects; +$AudioChannels[ $MessageAudioType ] = AudioChannelMessages; +$AudioChannels[ $MusicAudioType ] = AudioChannelMusic; + +function sfxOldChannelToGroup( %channel ) +{ + return $AudioChannels[ %channel ]; +} + +function sfxGroupToOldChannel( %group ) +{ + %id = %group.getId(); + for( %i = 0;; %i ++ ) + if( !isObject( $AudioChannels[ %i ] ) ) + return -1; + else if( $AudioChannels[ %i ].getId() == %id ) + return %i; + + return -1; +} + +function sfxSetMasterVolume( %volume ) +{ + AudioChannelMaster.setVolume( %volume ); +} + +function sfxGetMasterVolume( %volume ) +{ + return AudioChannelMaster.getVolume(); +} + +function sfxStopAll( %channel ) +{ + // Don't stop channel itself since that isn't quite what the function + // here intends. + + %channel = sfxOldChannelToGroup( %channel ); + if (isObject(%channel)) + { + foreach( %source in %channel ) + %source.stop(); + } +} + +function sfxGetChannelVolume( %channel ) +{ + %obj = sfxOldChannelToGroup( %channel ); + if( isObject( %obj ) ) + return %obj.getVolume(); +} + +function sfxSetChannelVolume( %channel, %volume ) +{ + %obj = sfxOldChannelToGroup( %channel ); + if( isObject( %obj ) ) + %obj.setVolume( %volume ); +} + +/*singleton SimSet( SFXPausedSet ); + + +/// Pauses the playback of active sound sources. +/// +/// @param %channels An optional word list of channel indices or an empty +/// string to pause sources on all channels. +/// @param %pauseSet An optional SimSet which is filled with the paused +/// sources. If not specified the global SfxSourceGroup +/// is used. +/// +/// @deprecated +/// +function sfxPause( %channels, %pauseSet ) +{ + // Did we get a set to populate? + if ( !isObject( %pauseSet ) ) + %pauseSet = SFXPausedSet; + + %count = SFXSourceSet.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %source = SFXSourceSet.getObject( %i ); + + %channel = sfxGroupToOldChannel( %source.getGroup() ); + if( %channels $= "" || findWord( %channels, %channel ) != -1 ) + { + %source.pause(); + %pauseSet.add( %source ); + } + } +} + + +/// Resumes the playback of paused sound sources. +/// +/// @param %pauseSet An optional SimSet which contains the paused sound +/// sources to be resumed. If not specified the global +/// SfxSourceGroup is used. +/// @deprecated +/// +function sfxResume( %pauseSet ) +{ + if ( !isObject( %pauseSet ) ) + %pauseSet = SFXPausedSet; + + %count = %pauseSet.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %source = %pauseSet.getObject( %i ); + %source.play(); + } + + // Clear our pause set... the caller is left + // to clear his own if he passed one. + %pauseSet.clear(); +}*/ diff --git a/Templates/BaseGame/game/core/canvas.cs b/Templates/BaseGame/game/core/canvas.cs new file mode 100644 index 000000000..b38cdccca --- /dev/null +++ b/Templates/BaseGame/game/core/canvas.cs @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function createCanvas(%windowTitle) +{ + if ($isDedicated) + { + GFXInit::createNullDevice(); + return true; + } + + // Create the Canvas + $GameCanvas = new GuiCanvas(Canvas) + { + displayWindow = $platform !$= "windows"; + }; + + // Set the window title + if (isObject(Canvas)) + { + Canvas.setWindowTitle(%windowTitle @ " - " @ $pref::Video::displayDevice); + configureCanvas(); + } + else + { + error("Canvas creation failed. Shutting down."); + quit(); + } +} + +// Constants for referencing video resolution preferences +$WORD::RES_X = 0; +$WORD::RES_Y = 1; +$WORD::FULLSCREEN = 2; +$WORD::BITDEPTH = 3; +$WORD::REFRESH = 4; +$WORD::AA = 5; + +function configureCanvas() +{ + // Setup a good default if we don't have one already. + if ($pref::Video::Resolution $= "") + $pref::Video::Resolution = "800 600"; + if ($pref::Video::FullScreen $= "") + $pref::Video::FullScreen = false; + if ($pref::Video::BitDepth $= "") + $pref::Video::BitDepth = "32"; + if ($pref::Video::RefreshRate $= "") + $pref::Video::RefreshRate = "60"; + if ($pref::Video::AA $= "") + $pref::Video::AA = "4"; + + %resX = $pref::Video::Resolution.x; + %resY = $pref::Video::Resolution.y; + %fs = $pref::Video::FullScreen; + %bpp = $pref::Video::BitDepth; + %rate = $pref::Video::RefreshRate; + %aa = $pref::Video::AA; + + if($cliFullscreen !$= "") { + %fs = $cliFullscreen; + $cliFullscreen = ""; + } + + echo("--------------"); + echo("Attempting to set resolution to \"" @ %resX SPC %resY SPC %fs SPC %bpp SPC %rate SPC %aa @ "\""); + + %deskRes = getDesktopResolution(); + %deskResX = getWord(%deskRes, $WORD::RES_X); + %deskResY = getWord(%deskRes, $WORD::RES_Y); + %deskResBPP = getWord(%deskRes, 2); + + // We shouldn't be getting this any more but just in case... + if (%bpp $= "Default") + %bpp = %deskResBPP; + + // Make sure we are running at a valid resolution + if (%fs $= "0" || %fs $= "false") + { + // Windowed mode has to use the same bit depth as the desktop + %bpp = %deskResBPP; + + // Windowed mode also has to run at a smaller resolution than the desktop + if ((%resX >= %deskResX) || (%resY >= %deskResY)) + { + warn("Warning: The requested windowed resolution is equal to or larger than the current desktop resolution. Attempting to find a better resolution"); + + %resCount = Canvas.getModeCount(); + for (%i = (%resCount - 1); %i >= 0; %i--) + { + %testRes = Canvas.getMode(%i); + %testResX = getWord(%testRes, $WORD::RES_X); + %testResY = getWord(%testRes, $WORD::RES_Y); + %testBPP = getWord(%testRes, $WORD::BITDEPTH); + + if (%testBPP != %bpp) + continue; + + if ((%testResX < %deskResX) && (%testResY < %deskResY)) + { + // This will work as our new resolution + %resX = %testResX; + %resY = %testResY; + + warn("Warning: Switching to \"" @ %resX SPC %resY SPC %bpp @ "\""); + + break; + } + } + } + } + + $pref::Video::Resolution = %resX SPC %resY; + $pref::Video::FullScreen = %fs; + $pref::Video::BitDepth = %bpp; + $pref::Video::RefreshRate = %rate; + $pref::Video::AA = %aa; + + if (%fs == 1 || %fs $= "true") + %fsLabel = "Yes"; + else + %fsLabel = "No"; + + echo("Accepted Mode: " NL + "--Resolution : " @ %resX SPC %resY NL + "--Full Screen : " @ %fsLabel NL + "--Bits Per Pixel : " @ %bpp NL + "--Refresh Rate : " @ %rate NL + "--AA TypeXLevel : " @ %aa NL + "--------------"); + + // Actually set the new video mode + Canvas.setVideoMode(%resX, %resY, %fs, %bpp, %rate, %aa); + + commandToServer('setClientAspectRatio', %resX, %resY); + + // AA piggybacks on the AA setting in $pref::Video::mode. + // We need to parse the setting between AA modes, and then it's level + // It's formatted as AATypexAALevel + // So, FXAAx4 or MLAAx2 + if ( isObject( FXAA_PostEffect ) ) + FXAA_PostEffect.isEnabled = ( %aa > 0 ) ? true : false; +} diff --git a/Templates/BaseGame/game/core/console/console.gui b/Templates/BaseGame/game/core/console/console.gui new file mode 100644 index 000000000..8c6c7f63a --- /dev/null +++ b/Templates/BaseGame/game/core/console/console.gui @@ -0,0 +1,51 @@ +new GuiControl(ConsoleDlg) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiConsoleEditCtrl(ConsoleEntry) { + profile = "ConsoleTextEditProfile"; + horizSizing = "width"; + vertSizing = "top"; + position = "0 462"; + extent = "640 18"; + minExtent = "8 8"; + visible = "1"; + altCommand = "ConsoleEntry::eval();"; + helpTag = "0"; + maxLength = "255"; + historySize = "40"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "1"; + useSiblingScroller = "1"; + }; + new GuiScrollCtrl() { + internalName = "Scroll"; + profile = "ConsoleScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 462"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOn"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiConsole( ConsoleMessageLogView ) { + profile = "GuiConsoleProfile"; + position = "0 0"; + }; + }; +}; diff --git a/Templates/BaseGame/game/core/console/main.cs b/Templates/BaseGame/game/core/console/main.cs new file mode 100644 index 000000000..b36dafd24 --- /dev/null +++ b/Templates/BaseGame/game/core/console/main.cs @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +exec("./profiles.cs"); +exec("./console.gui"); + +GlobalActionMap.bind("keyboard", "tilde", "toggleConsole"); + +function ConsoleEntry::eval() +{ + %text = trim(ConsoleEntry.getValue()); + if(%text $= "") + return; + + // If it's missing a trailing () and it's not a variable, + // append the parentheses. + if(strpos(%text, "(") == -1 && !isDefined(%text)) { + if(strpos(%text, "=") == -1 && strpos(%text, " ") == -1) { + if(strpos(%text, "{") == -1 && strpos(%text, "}") == -1) { + %text = %text @ "()"; + } + } + } + + // Append a semicolon if need be. + %pos = strlen(%text) - 1; + if(strpos(%text, ";", %pos) == -1 && strpos(%text, "}") == -1) { + %text = %text @ ";"; + } + + // Turn off warnings for assigning from void + // and evaluate the snippet. + if(!isDefined("$Con::warnVoidAssignment")) + %oldWarnVoidAssignment = true; + else + %oldWarnVoidAssignment = $Con::warnVoidAssignment; + $Con::warnVoidAssignment = false; + + echo("==>" @ %text); + if( !startsWith(%text, "function ") + && !startsWith(%text, "datablock ") + && !startsWith(%text, "foreach(") + && !startsWith(%text, "foreach$(") + && !startsWith(%text, "if(") + && !startsWith(%text, "while(") + && !startsWith(%text, "for(") + && !startsWith(%text, "switch(") + && !startsWith(%text, "switch$(")) + eval("%result = " @ %text); + else + eval(%text); + $Con::warnVoidAssignment = %oldWarnVoidAssignment; + + ConsoleEntry.setValue(""); + + // Echo result. + if(%result !$= "") + echo(%result); +} + +function ToggleConsole(%make) +{ + if (%make) { + if (ConsoleDlg.isAwake()) { + // Deactivate the console. + Canvas.popDialog(ConsoleDlg); + } else { + Canvas.pushDialog(ConsoleDlg, 99); + } + } +} + +function ConsoleDlg::hideWindow(%this) +{ + %this-->Scroll.setVisible(false); +} + +function ConsoleDlg::showWindow(%this) +{ + %this-->Scroll.setVisible(true); +} + +function ConsoleDlg::setAlpha( %this, %alpha) +{ + if (%alpha $= "") + ConsoleScrollProfile.fillColor = $ConsoleDefaultFillColor; + else + ConsoleScrollProfile.fillColor = getWords($ConsoleDefaultFillColor, 0, 2) SPC %alpha * 255.0; +} diff --git a/Templates/BaseGame/game/core/console/profiles.cs b/Templates/BaseGame/game/core/console/profiles.cs new file mode 100644 index 000000000..b83dd4fa7 --- /dev/null +++ b/Templates/BaseGame/game/core/console/profiles.cs @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +if(!isObject(GuiConsoleProfile)) +new GuiControlProfile(GuiConsoleProfile) +{ + fontType = ($platform $= "macos") ? "Monaco" : "Lucida Console"; + fontSize = ($platform $= "macos") ? 13 : 12; + fontColor = "255 255 255"; + fontColorHL = "0 255 255"; + fontColorNA = "255 0 0"; + fontColors[6] = "100 100 100"; + fontColors[7] = "100 100 0"; + fontColors[8] = "0 0 100"; + fontColors[9] = "0 100 0"; + category = "Core"; +}; + +if(!isObject(GuiConsoleTextProfile)) +new GuiControlProfile(GuiConsoleTextProfile) +{ + fontColor = "0 0 0"; + autoSizeWidth = true; + autoSizeHeight = true; + textOffset = "2 2"; + opaque = true; + fillColor = "255 255 255"; + border = true; + borderThickness = 1; + borderColor = "0 0 0"; + category = "Core"; +}; + +if(!isObject(ConsoleScrollProfile)) +new GuiControlProfile(ConsoleScrollProfile : GuiScrollProfile) +{ + opaque = true; + fillColor = "0 0 0 175"; + border = 1; + //borderThickness = 0; + borderColor = "0 0 0"; + category = "Core"; +}; + +if(!isObject(ConsoleTextEditProfile)) +new GuiControlProfile(ConsoleTextEditProfile : GuiTextEditProfile) +{ + fillColor = "242 241 240 255"; + fillColorHL = "255 255 255"; + category = "Core"; +}; diff --git a/Templates/BaseGame/game/core/cursor.cs b/Templates/BaseGame/game/core/cursor.cs new file mode 100644 index 000000000..f71bc023a --- /dev/null +++ b/Templates/BaseGame/game/core/cursor.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// Cursor toggle functions. +//--------------------------------------------------------------------------------------------- +$cursorControlled = true; +function showCursor() +{ + if ($cursorControlled) + lockMouse(false); + Canvas.cursorOn(); +} + +function hideCursor() +{ + if ($cursorControlled) + lockMouse(true); + Canvas.cursorOff(); +} + +//--------------------------------------------------------------------------------------------- +// In the CanvasCursor package we add some additional functionality to the built-in GuiCanvas +// class, of which the global Canvas object is an instance. In this case, the behavior we want +// is for the cursor to automatically display, except when the only guis visible want no +// cursor - usually the in game interface. +//--------------------------------------------------------------------------------------------- +package CanvasCursorPackage +{ + +//--------------------------------------------------------------------------------------------- +// checkCursor +// The checkCursor method iterates through all the root controls on the canvas checking each +// ones noCursor property. If the noCursor property exists as anything other than false or an +// empty string on every control, the cursor will be hidden. +//--------------------------------------------------------------------------------------------- +function GuiCanvas::checkCursor(%this) +{ + %count = %this.getCount(); + for(%i = 0; %i < %count; %i++) + { + %control = %this.getObject(%i); + if ((%control.noCursor $= "") || !%control.noCursor) + { + showCursor(); + return; + } + } + // If we get here, every control requested a hidden cursor, so we oblige. + hideCursor(); +} + +//--------------------------------------------------------------------------------------------- +// The following functions override the GuiCanvas defaults that involve changing the content +// of the Canvas. Basically, all we are doing is adding a call to checkCursor to each one. +//--------------------------------------------------------------------------------------------- +function GuiCanvas::setContent(%this, %ctrl) +{ + Parent::setContent(%this, %ctrl); + %this.checkCursor(); +} + +function GuiCanvas::pushDialog(%this, %ctrl, %layer, %center) +{ + Parent::pushDialog(%this, %ctrl, %layer, %center); + %this.checkCursor(); +} + +function GuiCanvas::popDialog(%this, %ctrl) +{ + Parent::popDialog(%this, %ctrl); + %this.checkCursor(); +} + +function GuiCanvas::popLayer(%this, %layer) +{ + Parent::popLayer(%this, %layer); + %this.checkCursor(); +} + +}; + +activatePackage(CanvasCursorPackage); diff --git a/Templates/BaseGame/game/core/fonts/Arial 10 (ansi).uft b/Templates/BaseGame/game/core/fonts/Arial 10 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..2b564950050cfca995e66f048e0523428ff493b0 GIT binary patch literal 412 zcmZQ(U|?W%EXqvG;R3Qi07P>@F%ytx24VyN@mQf;5Dk%qsR4%?)Qy83;)lEIri7m zFZoExihWaFY>GaAyHfb_#$7>4rhD3-7VX{JcRHkY<;8EhWjEc^(!MS|x7jKnT~tu; z`y-fhfi55e9MJ#4lvSq~c?$DU2jYrb$q5Y1ZVG9@ Ja2IA^0RUczP(}a% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/fonts/Arial 12 (ansi).uft b/Templates/BaseGame/game/core/fonts/Arial 12 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..67a177016766c6f06b5b601c3336b9fea2ac75ba GIT binary patch literal 62 jcmZQ(U|?W%EXqvG;Q_Kh07P>_F*8U23jU*ldXO*xH#ZTF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/fonts/Arial 14 (ansi).uft b/Templates/BaseGame/game/core/fonts/Arial 14 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..a5fc4ef5fbeef20ba072feb6b3043c3ad95a1ea4 GIT binary patch literal 5123 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/fonts/Arial 36 (ansi).uft b/Templates/BaseGame/game/core/fonts/Arial 36 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..968441ce3b27703632299ab9837977bb7ee35234 GIT binary patch literal 1046 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 EUoz6V=Kufz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/fonts/ArialBold 14 (ansi).uft b/Templates/BaseGame/game/core/fonts/ArialBold 14 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..08d83901e9e130928683c6aff714b60998ca2112 GIT binary patch literal 2172 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/fonts/Lucida Console 12 (ansi).uft b/Templates/BaseGame/game/core/fonts/Lucida Console 12 (ansi).uft new file mode 100644 index 0000000000000000000000000000000000000000..bd148fa9b437f7e493d59f8e4bfa022ce8ef5ee4 GIT binary patch literal 5850 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 WJqEt_|9@@CK{SG=4t7(l&iOxEekq0k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/gfxData/clouds.cs b/Templates/BaseGame/game/core/gfxData/clouds.cs new file mode 100644 index 000000000..00d56d6d3 --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/clouds.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// CloudLayer +//------------------------------------------------------------------------------ + +singleton ShaderData( CloudLayerShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/cloudLayerV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/cloudLayerP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/cloudLayerV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/cloudLayerP.glsl"; + + samplerNames[0] = "$normalHeightMap"; + + pixVersion = 2.0; +}; + +//------------------------------------------------------------------------------ +// BasicClouds +//------------------------------------------------------------------------------ + +singleton ShaderData( BasicCloudsShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/basicCloudsV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/basicCloudsP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/basicCloudsV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/basicCloudsP.glsl"; + + samplerNames[0] = "$diffuseMap"; + + pixVersion = 2.0; +}; diff --git a/Templates/BaseGame/game/core/gfxData/commonMaterialData.cs b/Templates/BaseGame/game/core/gfxData/commonMaterialData.cs new file mode 100644 index 000000000..c5d8ef5bc --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/commonMaterialData.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Anim flag settings - must match material.h +// These cannot be enumed through script becuase it cannot +// handle the "|" operation for combining them together +// ie. Scroll | Wave does not work. +//----------------------------------------------------------------------------- +$scroll = 1; +$rotate = 2; +$wave = 4; +$scale = 8; +$sequence = 16; + + +// Common stateblock definitions +new GFXSamplerStateData(SamplerClampLinear) +{ + textureColorOp = GFXTOPModulate; + addressModeU = GFXAddressClamp; + addressModeV = GFXAddressClamp; + addressModeW = GFXAddressClamp; + magFilter = GFXTextureFilterLinear; + minFilter = GFXTextureFilterLinear; + mipFilter = GFXTextureFilterLinear; +}; + +new GFXSamplerStateData(SamplerClampPoint) +{ + textureColorOp = GFXTOPModulate; + addressModeU = GFXAddressClamp; + addressModeV = GFXAddressClamp; + addressModeW = GFXAddressClamp; + magFilter = GFXTextureFilterPoint; + minFilter = GFXTextureFilterPoint; + mipFilter = GFXTextureFilterPoint; +}; + +new GFXSamplerStateData(SamplerWrapLinear) +{ + textureColorOp = GFXTOPModulate; + addressModeU = GFXTextureAddressWrap; + addressModeV = GFXTextureAddressWrap; + addressModeW = GFXTextureAddressWrap; + magFilter = GFXTextureFilterLinear; + minFilter = GFXTextureFilterLinear; + mipFilter = GFXTextureFilterLinear; +}; + +new GFXSamplerStateData(SamplerWrapPoint) +{ + textureColorOp = GFXTOPModulate; + addressModeU = GFXTextureAddressWrap; + addressModeV = GFXTextureAddressWrap; + addressModeW = GFXTextureAddressWrap; + magFilter = GFXTextureFilterPoint; + minFilter = GFXTextureFilterPoint; + mipFilter = GFXTextureFilterPoint; +}; diff --git a/Templates/BaseGame/game/core/gfxData/scatterSky.cs b/Templates/BaseGame/game/core/gfxData/scatterSky.cs new file mode 100644 index 000000000..5add01d8b --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/scatterSky.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +new GFXStateBlockData( ScatterSkySBData ) +{ + cullMode = "GFXCullNone"; + + zDefined = true; + zEnable = true; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; + vertexColorEnable = true; +}; + +singleton ShaderData( ScatterSkyShaderData ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/scatterSkyV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/scatterSkyP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/scatterSkyV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/scatterSkyP.glsl"; + + samplerNames[0] = "$nightSky"; + + pixVersion = 2.0; +}; diff --git a/Templates/BaseGame/game/core/gfxData/shaders.cs b/Templates/BaseGame/game/core/gfxData/shaders.cs new file mode 100644 index 000000000..5f733abd0 --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/shaders.cs @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// This file contains shader data necessary for various engine utility functions +//----------------------------------------------------------------------------- + + +singleton ShaderData( ParticlesShaderData ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/particlesV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/particlesP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/particlesV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/particlesP.glsl"; + + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$prepassTex"; + samplerNames[2] = "$paraboloidLightMap"; + + pixVersion = 2.0; +}; + +singleton ShaderData( OffscreenParticleCompositeShaderData ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/particleCompositeV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/particleCompositeP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/particleCompositeV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/particleCompositeP.glsl"; + + samplerNames[0] = "$colorSource"; + samplerNames[1] = "$edgeSource"; + + pixVersion = 2.0; +}; + +//----------------------------------------------------------------------------- +// Planar Reflection +//----------------------------------------------------------------------------- +new ShaderData( ReflectBump ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/planarReflectBumpV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/planarReflectBumpP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/planarReflectBumpV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/planarReflectBumpP.glsl"; + + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$refractMap"; + samplerNames[2] = "$bumpMap"; + + pixVersion = 2.0; +}; + +new ShaderData( Reflect ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/planarReflectV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/planarReflectP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/planarReflectV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/planarReflectP.glsl"; + + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$refractMap"; + + pixVersion = 1.4; +}; + +//----------------------------------------------------------------------------- +// fxFoliageReplicator +//----------------------------------------------------------------------------- +new ShaderData( fxFoliageReplicatorShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/fxFoliageReplicatorV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/fxFoliageReplicatorP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/gl/fxFoliageReplicatorV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/gl/fxFoliageReplicatorP.glsl"; + + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$alphaMap"; + + pixVersion = 1.4; +}; + +singleton ShaderData( VolumetricFogPrePassShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogPreV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogPreP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogPreV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogPreP.glsl"; + + pixVersion = 3.0; +}; +singleton ShaderData( VolumetricFogShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogP.glsl"; + + samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$depthBuffer"; + samplerNames[2] = "$frontBuffer"; + samplerNames[3] = "$density"; + + pixVersion = 3.0; +}; +singleton ShaderData( VolumetricFogReflectionShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogPreV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/VFogRefl.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogPreV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/VolumetricFog/gl/VFogRefl.glsl"; + + pixVersion = 3.0; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/core/gfxData/terrainBlock.cs b/Templates/BaseGame/game/core/gfxData/terrainBlock.cs new file mode 100644 index 000000000..69802b1da --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/terrainBlock.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// Used when generating the blended base texture. +singleton ShaderData( TerrainBlendShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/terrain/blendV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/terrain/blendP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/terrain/gl/blendV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/terrain/gl/blendP.glsl"; + + samplerNames[0] = "layerTex"; + samplerNames[1] = "textureMap"; + + pixVersion = 2.0; +}; diff --git a/Templates/BaseGame/game/core/gfxData/water.cs b/Templates/BaseGame/game/core/gfxData/water.cs new file mode 100644 index 000000000..a7332d0c4 --- /dev/null +++ b/Templates/BaseGame/game/core/gfxData/water.cs @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// Water +//----------------------------------------------------------------------------- + +singleton ShaderData( WaterShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/water/waterV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/water/waterP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/water/gl/waterV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/water/gl/waterP.glsl"; + + samplerNames[0] = "$bumpMap"; // noise + samplerNames[1] = "$prepassTex"; // #prepass + samplerNames[2] = "$reflectMap"; // $reflectbuff + samplerNames[3] = "$refractBuff"; // $backbuff + samplerNames[4] = "$skyMap"; // $cubemap + samplerNames[5] = "$foamMap"; // foam + samplerNames[6] = "$depthGradMap"; // depthMap ( color gradient ) + + pixVersion = 3.0; +}; + +new GFXSamplerStateData(WaterSampler) +{ + textureColorOp = GFXTOPModulate; + addressModeU = GFXAddressWrap; + addressModeV = GFXAddressWrap; + addressModeW = GFXAddressWrap; + magFilter = GFXTextureFilterLinear; + minFilter = GFXTextureFilterAnisotropic; + mipFilter = GFXTextureFilterLinear; + maxAnisotropy = 4; +}; + +singleton GFXStateBlockData( WaterStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = WaterSampler; // noise + samplerStates[1] = SamplerClampPoint; // #prepass + samplerStates[2] = SamplerClampLinear; // $reflectbuff + samplerStates[3] = SamplerClampPoint; // $backbuff + samplerStates[4] = SamplerWrapLinear; // $cubemap + samplerStates[5] = SamplerWrapLinear; // foam + samplerStates[6] = SamplerClampLinear; // depthMap ( color gradient ) + cullDefined = true; + cullMode = "GFXCullCCW"; +}; + +singleton GFXStateBlockData( UnderWaterStateBlock : WaterStateBlock ) +{ + cullMode = "GFXCullCW"; +}; + +singleton CustomMaterial( WaterMat ) +{ + sampler["prepassTex"] = "#prepass"; + sampler["reflectMap"] = "$reflectbuff"; + sampler["refractBuff"] = "$backbuff"; + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = ""; + //sampler["skyMap"] = ""; + //sampler["foamMap"] = ""; + //sampler["depthGradMap"] = ""; + + shader = WaterShader; + stateBlock = WaterStateBlock; + version = 3.0; + + useAnisotropic[0] = true; +}; + +//----------------------------------------------------------------------------- +// Underwater +//----------------------------------------------------------------------------- + +singleton ShaderData( UnderWaterShader : WaterShader ) +{ + defines = "UNDERWATER"; +}; + +singleton CustomMaterial( UnderwaterMat ) +{ + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = "core/art/water/noise02"; + //sampler["foamMap"] = "core/art/water/foam"; + + sampler["prepassTex"] = "#prepass"; + sampler["refractBuff"] = "$backbuff"; + + shader = UnderWaterShader; + stateBlock = UnderWaterStateBlock; + specular = "0.75 0.75 0.75 1.0"; + specularPower = 48.0; + version = 3.0; +}; + +//----------------------------------------------------------------------------- +// Basic Water +//----------------------------------------------------------------------------- + +singleton ShaderData( WaterBasicShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/water/waterBasicV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/water/waterBasicP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/water/gl/waterBasicV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/water/gl/waterBasicP.glsl"; + + samplerNames[0] = "$bumpMap"; + samplerNames[2] = "$reflectMap"; + samplerNames[3] = "$refractBuff"; + samplerNames[4] = "$skyMap"; + samplerNames[5] = "$depthGradMap"; + + pixVersion = 2.0; +}; + +singleton GFXStateBlockData( WaterBasicStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = WaterSampler; // noise + samplerStates[2] = SamplerClampLinear; // $reflectbuff + samplerStates[3] = SamplerClampPoint; // $backbuff + samplerStates[4] = SamplerWrapLinear; // $cubemap + cullDefined = true; + cullMode = "GFXCullCCW"; +}; + +singleton GFXStateBlockData( UnderWaterBasicStateBlock : WaterBasicStateBlock ) +{ + cullMode = "GFXCullCW"; +}; + +singleton CustomMaterial( WaterBasicMat ) +{ + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = "core/art/water/noise02"; + //sampler["skyMap"] = "$cubemap"; + + //sampler["prepassTex"] = "#prepass"; + sampler["reflectMap"] = "$reflectbuff"; + sampler["refractBuff"] = "$backbuff"; + + cubemap = NewLevelSkyCubemap; + shader = WaterBasicShader; + stateBlock = WaterBasicStateBlock; + version = 2.0; +}; + +//----------------------------------------------------------------------------- +// Basic UnderWater +//----------------------------------------------------------------------------- + +singleton ShaderData( UnderWaterBasicShader : WaterBasicShader) +{ + defines = "UNDERWATER"; +}; + +singleton CustomMaterial( UnderwaterBasicMat ) +{ + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = "core/art/water/noise02"; + //samplers["skyMap"] = "$cubemap"; + + //sampler["prepassTex"] = "#prepass"; + sampler["refractBuff"] = "$backbuff"; + + shader = UnderWaterBasicShader; + stateBlock = UnderWaterBasicStateBlock; + version = 2.0; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/core/gfxprofile/D3D9.ATITechnologiesInc.cs b/Templates/BaseGame/game/core/gfxprofile/D3D9.ATITechnologiesInc.cs new file mode 100644 index 000000000..10c6bdf5b --- /dev/null +++ b/Templates/BaseGame/game/core/gfxprofile/D3D9.ATITechnologiesInc.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// ATI Vendor Profile Script +// +// This script is responsible for setting global +// capability strings based on the nVidia vendor. + +if(GFXCardProfiler::getVersion() < 64.44) +{ + $GFX::OutdatedDrivers = true; + $GFX::OutdatedDriversLink = "You can get newer drivers here.."; +} +else +{ + $GFX::OutdatedDrivers = false; +} diff --git a/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.GeForce8600.cs b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.GeForce8600.cs new file mode 100644 index 000000000..328788dac --- /dev/null +++ b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.GeForce8600.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// nVidia Vendor Profile Script +// +// This script is responsible for setting global +// capability strings based on the nVidia vendor. + +if(GFXCardProfiler::getVersion() < 1.2) +{ + $GFX::OutdatedDrivers = true; + $GFX::OutdatedDriversLink = "You can get newer drivers here.."; +} +else +{ + $GFX::OutdatedDrivers = false; +} diff --git a/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.QuadroFXGo1000.cs b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.QuadroFXGo1000.cs new file mode 100644 index 000000000..5681b2f6d --- /dev/null +++ b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.QuadroFXGo1000.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// nVidia Vendor Profile Script +// +// This script is responsible for setting global +// capability strings based on the nVidia vendor. + +if(GFXCardProfiler::getVersion() < 53.82) +{ + $GFX::OutdatedDrivers = true; + $GFX::OutdatedDriversLink = "You can get newer drivers here.."; +} +else +{ + $GFX::OutdatedDrivers = false; +} + +// Silly card has trouble with this! +GFXCardProfiler::setCapability("autoMipmapLevel", false); diff --git a/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.cs b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.cs new file mode 100644 index 000000000..b33b8d5d3 --- /dev/null +++ b/Templates/BaseGame/game/core/gfxprofile/D3D9.NVIDIA.cs @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// nVidia Vendor Profile Script +// +// This script is responsible for setting global +// capability strings based on the nVidia vendor. + +if(GFXCardProfiler::getVersion() < 56.72) +{ + $GFX::OutdatedDrivers = true; + $GFX::OutdatedDriversLink = "You can get newer drivers here.."; +} +else +{ + $GFX::OutdatedDrivers = false; +} diff --git a/Templates/BaseGame/game/core/gfxprofile/D3D9.cs b/Templates/BaseGame/game/core/gfxprofile/D3D9.cs new file mode 100644 index 000000000..e1e299341 --- /dev/null +++ b/Templates/BaseGame/game/core/gfxprofile/D3D9.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Direct3D 9 Renderer Profile Script +// +// This script is responsible for setting global +// capability strings based on the D3D9 renderer type. \ No newline at end of file diff --git a/Templates/BaseGame/game/core/globals.cs b/Templates/BaseGame/game/core/globals.cs new file mode 100644 index 000000000..d730d63a4 --- /dev/null +++ b/Templates/BaseGame/game/core/globals.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// DInput keyboard, mouse, and joystick prefs +// ---------------------------------------------------------------------------- + +$pref::Input::MouseEnabled = 1; +$pref::Input::LinkMouseSensitivity = 1; +$pref::Input::KeyboardEnabled = 1; +$pref::Input::KeyboardTurnSpeed = 0.1; +$pref::Input::JoystickEnabled = 0; + +// ---------------------------------------------------------------------------- +// Video Preferences +// ---------------------------------------------------------------------------- + +// Set directory paths for various data or default images. +$pref::Video::ProfilePath = "core/gfxprofile"; +$pref::Video::missingTexturePath = "core/images/missingTexture.png"; +$pref::Video::unavailableTexturePath = "core/images/unavailable.png"; +$pref::Video::warningTexturePath = "core/images/warnMat.dds"; + +$pref::Video::disableVerticalSync = 1; +$pref::Video::mode = "800 600 false 32 60 4"; +$pref::Video::defaultFenceCount = 0; + +// This disables the hardware FSAA/MSAA so that we depend completely on the FXAA +// post effect which works on all cards and in deferred mode. Note that the new +// Intel Hybrid graphics on laptops will fail to initialize when hardware AA is +// enabled... so you've been warned. +$pref::Video::disableHardwareAA = true; + +$pref::Video::disableNormalmapping = false; +$pref::Video::disablePixSpecular = false; +$pref::Video::disableCubemapping = false; +$pref::Video::disableParallaxMapping = false; + +// The number of mipmap levels to drop on loaded textures to reduce video memory +// usage. It will skip any textures that have been defined as not allowing down +// scaling. +$pref::Video::textureReductionLevel = 0; + +$pref::Video::defaultAnisotropy = 1; +//$pref::Video::Gamma = 1.0; + +/// AutoDetect graphics quality levels the next startup. +$pref::Video::autoDetect = 1; + +// ---------------------------------------------------------------------------- +// Shader stuff +// ---------------------------------------------------------------------------- + +// This is the path used by ShaderGen to cache procedural shaders. If left +// blank ShaderGen will only cache shaders to memory and not to disk. +$shaderGen::cachePath = ""; + +// Uncomment to disable ShaderGen, useful when debugging +//$ShaderGen::GenNewShaders = false; + +// Uncomment to dump disassembly for any shader that is compiled to disk. These +// will appear as shadername_dis.txt in the same path as the shader file. +//$gfx::disassembleAllShaders = true; + +// ---------------------------------------------------------------------------- +// Lighting and shadowing +// ---------------------------------------------------------------------------- + +// Uncomment to enable AdvancedLighting on the Mac (T3D 2009 Beta 3) +//$pref::machax::enableAdvancedLighting = true; + +$sceneLighting::cacheSize = 20000; +$sceneLighting::purgeMethod = "lastCreated"; +$sceneLighting::cacheLighting = 1; + +$pref::Shadows::textureScalar = 1.0; +$pref::Shadows::disable = false; + +// Sets the shadow filtering mode. +// None - Disables filtering. +// SoftShadow - Does a simple soft shadow +// SoftShadowHighQuality +$pref::Shadows::filterMode = "SoftShadow"; diff --git a/Templates/BaseGame/game/core/helperFunctions.cs b/Templates/BaseGame/game/core/helperFunctions.cs new file mode 100644 index 000000000..8559eda88 --- /dev/null +++ b/Templates/BaseGame/game/core/helperFunctions.cs @@ -0,0 +1,528 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------ +// Check if a script file exists, compiled or not. +function isScriptFile(%path) +{ + if( isFile(%path @ ".dso") || isFile(%path) ) + return true; + + return false; +} + +function loadMaterials() +{ + // Load any materials files for which we only have DSOs. + + for( %file = findFirstFile( "*/materials.cs.dso" ); + %file !$= ""; + %file = findNextFile( "*/materials.cs.dso" )) + { + // Only execute, if we don't have the source file. + %csFileName = getSubStr( %file, 0, strlen( %file ) - 4 ); + if( !isFile( %csFileName ) ) + exec( %csFileName ); + } + + // Load all source material files. + + for( %file = findFirstFile( "*/materials.cs" ); + %file !$= ""; + %file = findNextFile( "*/materials.cs" )) + { + exec( %file ); + } + + // Load all materials created by the material editor if + // the folder exists + if( IsDirectory( "materialEditor" ) ) + { + for( %file = findFirstFile( "materialEditor/*.cs.dso" ); + %file !$= ""; + %file = findNextFile( "materialEditor/*.cs.dso" )) + { + // Only execute, if we don't have the source file. + %csFileName = getSubStr( %file, 0, strlen( %file ) - 4 ); + if( !isFile( %csFileName ) ) + exec( %csFileName ); + } + + for( %file = findFirstFile( "materialEditor/*.cs" ); + %file !$= ""; + %file = findNextFile( "materialEditor/*.cs" )) + { + exec( %file ); + } + } +} + +function reloadMaterials() +{ + reloadTextures(); + loadMaterials(); + reInitMaterials(); +} + +function loadDatablockFiles( %datablockFiles, %recurse ) +{ + if ( %recurse ) + { + recursiveLoadDatablockFiles( %datablockFiles, 9999 ); + return; + } + + %count = %datablockFiles.count(); + for ( %i=0; %i < %count; %i++ ) + { + %file = %datablockFiles.getKey( %i ); + if ( !isFile(%file @ ".dso") && !isFile(%file) ) + continue; + + exec( %file ); + } + + // Destroy the incoming list. + //%datablockFiles.delete(); +} + +function recursiveLoadDatablockFiles( %datablockFiles, %previousErrors ) +{ + %reloadDatablockFiles = new ArrayObject(); + + // Keep track of the number of datablocks that + // failed during this pass. + %failedDatablocks = 0; + + // Try re-executing the list of datablock files. + %count = %datablockFiles.count(); + for ( %i=0; %i < %count; %i++ ) + { + %file = %datablockFiles.getKey( %i ); + if ( !isFile(%file @ ".dso") && !isFile(%file) ) + continue; + + // Start counting copy constructor creation errors. + $Con::objectCopyFailures = 0; + + exec( %file ); + + // If errors occured then store this file for re-exec later. + if ( $Con::objectCopyFailures > 0 ) + { + %reloadDatablockFiles.add( %file ); + %failedDatablocks = %failedDatablocks + $Con::objectCopyFailures; + } + } + + // Clear the object copy failure counter so that + // we get console error messages again. + $Con::objectCopyFailures = -1; + + // Delete the old incoming list... we're done with it. + //%datablockFiles.delete(); + + // If we still have datablocks to retry. + %newCount = %reloadDatablockFiles.count(); + if ( %newCount > 0 ) + { + // If the datablock failures have not been reduced + // from the last pass then we must have a real syntax + // error and not just a bad dependancy. + if ( %previousErrors > %failedDatablocks ) + recursiveLoadDatablockFiles( %reloadDatablockFiles, %failedDatablocks ); + + else + { + // Since we must have real syntax errors do one + // last normal exec to output error messages. + loadDatablockFiles( %reloadDatablockFiles, false ); + } + + return; + } + + // Cleanup the empty reload list. + %reloadDatablockFiles.delete(); +} + +function getPrefpath() +{ + %temp = getUserHomeDirectory(); + echo(%temp); + if(!isDirectory(%temp)) + { + %temp = getUserDataDirectory(); + echo(%temp); + if(!isDirectory(%temp)) + { + $prefpath = "data"; + } + else + { + //put it in appdata/roaming + $prefpath = %temp @ "/" @ $appName @ "/preferences"; + } + } + else + { + //put it in user/documents + $prefPath = %temp @ "/" @ $appName @ "/preferences"; + } + return $prefPath; +} + +function updateTSShapeLoadProgress(%progress, %msg) +{ + // Check if the loading GUI is visible and use that instead of the + // separate import progress GUI if possible + if ( isObject(LoadingGui) && LoadingGui.isAwake() ) + { + // Save/Restore load progress at the start/end of the import process + if ( %progress == 0 ) + { + ColladaImportProgress.savedProgress = LoadingProgress.getValue(); + ColladaImportProgress.savedText = LoadingProgressTxt.getValue(); + + ColladaImportProgress.msgPrefix = "Importing " @ %msg; + %msg = "Reading file into memory..."; + } + else if ( %progress == 1.0 ) + { + LoadingProgress.setValue( ColladaImportProgress.savedProgress ); + LoadingProgressTxt.setValue( ColladaImportProgress.savedText ); + } + + %msg = ColladaImportProgress.msgPrefix @ ": " @ %msg; + + %progressCtrl = LoadingProgress; + %textCtrl = LoadingProgressTxt; + } + else + { + //it's probably the editors using it + if(isFunction("updateToolTSShapeLoadProgress")) + { + updateToolTSShapeLoadProgress(%progress, %msg); + } + } + + // Update progress indicators + if (%progress == 0) + { + %progressCtrl.setValue(0.001); + %textCtrl.setText(%msg); + } + else if (%progress != 1.0) + { + %progressCtrl.setValue(%progress); + %textCtrl.setText(%msg); + } + + Canvas.repaint(33); +} + +/// A helper function which will return the ghosted client object +/// from a server object when connected to a local server. +function serverToClientObject( %serverObject ) +{ + assert( isObject( LocalClientConnection ), "serverToClientObject() - No local client connection found!" ); + assert( isObject( ServerConnection ), "serverToClientObject() - No server connection found!" ); + + %ghostId = LocalClientConnection.getGhostId( %serverObject ); + if ( %ghostId == -1 ) + return 0; + + return ServerConnection.resolveGhostID( %ghostId ); +} + +//---------------------------------------------------------------------------- +// Debug commands +//---------------------------------------------------------------------------- + +function netSimulateLag( %msDelay, %packetLossPercent ) +{ + if ( %packetLossPercent $= "" ) + %packetLossPercent = 0; + + commandToServer( 'NetSimulateLag', %msDelay, %packetLossPercent ); +} + +//Various client functions + +function validateDatablockName(%name) +{ + // remove whitespaces at beginning and end + %name = trim( %name ); + + // remove numbers at the beginning + %numbers = "0123456789"; + while( strlen(%name) > 0 ) + { + // the first character + %firstChar = getSubStr( %name, 0, 1 ); + // if the character is a number remove it + if( strpos( %numbers, %firstChar ) != -1 ) + { + %name = getSubStr( %name, 1, strlen(%name) -1 ); + %name = ltrim( %name ); + } + else + break; + } + + // replace whitespaces with underscores + %name = strreplace( %name, " ", "_" ); + + // remove any other invalid characters + %invalidCharacters = "-+*/%$&§=()[].?\"#,;!~<>|°^{}"; + %name = stripChars( %name, %invalidCharacters ); + + if( %name $= "" ) + %name = "Unnamed"; + + return %name; +} + +//-------------------------------------------------------------------------- +// Finds location of %word in %text, starting at %start. Works just like strPos +//-------------------------------------------------------------------------- + +function wordPos(%text, %word, %start) +{ + if (%start $= "") %start = 0; + + if (strpos(%text, %word, 0) == -1) return -1; + %count = getWordCount(%text); + if (%start >= %count) return -1; + for (%i = %start; %i < %count; %i++) + { + if (getWord( %text, %i) $= %word) return %i; + } + return -1; +} + +//-------------------------------------------------------------------------- +// Finds location of %field in %text, starting at %start. Works just like strPos +//-------------------------------------------------------------------------- + +function fieldPos(%text, %field, %start) +{ + if (%start $= "") %start = 0; + + if (strpos(%text, %field, 0) == -1) return -1; + %count = getFieldCount(%text); + if (%start >= %count) return -1; + for (%i = %start; %i < %count; %i++) + { + if (getField( %text, %i) $= %field) return %i; + } + return -1; +} + +//-------------------------------------------------------------------------- +// returns the text in a file with "\n" at the end of each line +//-------------------------------------------------------------------------- + +function loadFileText( %file) +{ + %fo = new FileObject(); + %fo.openForRead(%file); + %text = ""; + while(!%fo.isEOF()) + { + %text = %text @ %fo.readLine(); + if (!%fo.isEOF()) %text = %text @ "\n"; + } + + %fo.delete(); + return %text; +} + +function setValueSafe(%dest, %val) +{ + %cmd = %dest.command; + %alt = %dest.altCommand; + %dest.command = ""; + %dest.altCommand = ""; + + %dest.setValue(%val); + + %dest.command = %cmd; + %dest.altCommand = %alt; +} + +function shareValueSafe(%source, %dest) +{ + setValueSafe(%dest, %source.getValue()); +} + +function shareValueSafeDelay(%source, %dest, %delayMs) +{ + schedule(%delayMs, 0, shareValueSafe, %source, %dest); +} + + +//------------------------------------------------------------------------------ +// An Aggregate Control is a plain GuiControl that contains other controls, +// which all share a single job or represent a single value. +//------------------------------------------------------------------------------ + +// AggregateControl.setValue( ) propagates the value to any control that has an +// internal name. +function AggregateControl::setValue(%this, %val, %child) +{ + for(%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + if( %obj == %child ) + continue; + + if(%obj.internalName !$= "") + setValueSafe(%obj, %val); + } +} + +// AggregateControl.getValue() uses the value of the first control that has an +// internal name, if it has not cached a value via .setValue +function AggregateControl::getValue(%this) +{ + for(%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + if(%obj.internalName !$= "") + { + //error("obj = " @ %obj.getId() @ ", " @ %obj.getName() @ ", " @ %obj.internalName ); + //error(" value = " @ %obj.getValue()); + return %obj.getValue(); + } + } +} + +// AggregateControl.updateFromChild( ) is called by child controls to propagate +// a new value, and to trigger the onAction() callback. +function AggregateControl::updateFromChild(%this, %child) +{ + %val = %child.getValue(); + if(%val == mCeil(%val)){ + %val = mCeil(%val); + }else{ + if ( %val <= -100){ + %val = mCeil(%val); + }else if ( %val <= -10){ + %val = mFloatLength(%val, 1); + }else if ( %val < 0){ + %val = mFloatLength(%val, 2); + }else if ( %val >= 1000){ + %val = mCeil(%val); + }else if ( %val >= 100){ + %val = mFloatLength(%val, 1); + }else if ( %val >= 10){ + %val = mFloatLength(%val, 2); + }else if ( %val > 0){ + %val = mFloatLength(%val, 3); + } + } + %this.setValue(%val, %child); + %this.onAction(); +} + +// default onAction stub, here only to prevent console spam warnings. +function AggregateControl::onAction(%this) +{ +} + +// call a method on all children that have an internalName and that implement the method. +function AggregateControl::callMethod(%this, %method, %args) +{ + for(%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + if(%obj.internalName !$= "" && %obj.isMethod(%method)) + eval(%obj @ "." @ %method @ "( " @ %args @ " );"); + } + +} + +// A function used in order to easily parse the MissionGroup for classes . I'm pretty +// sure at this point the function can be easily modified to search the any group as well. +function parseMissionGroup( %className, %childGroup ) +{ + if( getWordCount( %childGroup ) == 0) + %currentGroup = "MissionGroup"; + else + %currentGroup = %childGroup; + + for(%i = 0; %i < (%currentGroup).getCount(); %i++) + { + if( (%currentGroup).getObject(%i).getClassName() $= %className ) + return true; + + if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" ) + { + if( parseMissionGroup( %className, (%currentGroup).getObject(%i).getId() ) ) + return true; + } + } +} + +// A variation of the above used to grab ids from the mission group based on classnames +function parseMissionGroupForIds( %className, %childGroup ) +{ + if( getWordCount( %childGroup ) == 0) + %currentGroup = "MissionGroup"; + else + %currentGroup = %childGroup; + + for(%i = 0; %i < (%currentGroup).getCount(); %i++) + { + if( (%currentGroup).getObject(%i).getClassName() $= %className ) + %classIds = %classIds @ (%currentGroup).getObject(%i).getId() @ " "; + + if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" ) + %classIds = %classIds @ parseMissionGroupForIds( %className, (%currentGroup).getObject(%i).getId()); + } + return trim( %classIds ); +} + +//------------------------------------------------------------------------------ +// Altered Version of TGB's QuickEditDropDownTextEditCtrl +//------------------------------------------------------------------------------ + +function QuickEditDropDownTextEditCtrl::onRenameItem( %this ) +{ +} + +function QuickEditDropDownTextEditCtrl::updateFromChild( %this, %ctrl ) +{ + if( %ctrl.internalName $= "PopUpMenu" ) + { + %this->TextEdit.setText( %ctrl.getText() ); + } + else if ( %ctrl.internalName $= "TextEdit" ) + { + %popup = %this->PopupMenu; + %popup.changeTextById( %popup.getSelected(), %ctrl.getText() ); + %this.onRenameItem(); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/images/button.png b/Templates/BaseGame/game/core/images/button.png new file mode 100644 index 0000000000000000000000000000000000000000..1c7361e25e5f19d68d0029199ada0b836793d137 GIT binary patch literal 1153 zcmV-{1b+L8P)&+)-D51G}Jb<|zhCP|jOI zUg3P882CUrPXV_@d4p}tpTCXQ+XeGZ<3=<}1sfal!sGmD#BGeMudhRGO$~99qB={_ z3B!?xd^I*w1*0=FGmuKBp`){t60z3`!;y!4oJXB_D!Zm&(Au5w!fj~eXmH*?# zs7(5xrQ&%(#>rJxRl$aa2B@p6qvY=HZsFSy&tx*by}iAl2X>1e(r|NgGc+|d0Z~Lj z{9Du*ueP=pR##VtnF62N?S}gLda&E=l-SwWHLbehW5e4St1sNSPFfafuEiKds zPkMS_b!A0Id`#AlIG%@GE(c>{W7M%Upy1kVHXB&2){D2zVkzOt3`8j_vG5A69MVuT zlgg&HidbOI5-RG%$}hp3(>ORd2u`OHs;jHvX>Tw5Tw2l*wfOk>c=0qw5{bl>-Wrd` zOY=fF9EQ!!%@QAIh2c1PY3WC4UWmnFK%|%#CMG6?c|pZ*Szai+UXWS6fNg$$9{SW? z+ZgV0xoEwh=F&o5!23TwY;A3&WY%jN;<#IVeSJ4jFU-%)K|J<{g4P5y~7K=oBy<3-B0;jY=*TAxabYA$i-pZKMXUUn;rc6~N=6 zVc*NP(N;ykt5mL7aNs<1ZYb!O2M-?=friG$P;!4?=L0Ps{{4$ORu-9j<$r zKS-w_o6TNI!6|uJDn&CDtvV!(uH-=^mE6xJ{_apB_F7?hI-SZQALk+VPk;dcQd{ug TkdaD=00000NkvXXu0mjfP6x2*Ap8)%9tb4lrk)-I`3P~()5{G20&{aS0<5jZ`C)ELXhV>{HfzWs?B~!vRsd2ql|mzS}alL-(@XIMsKQqBI5idZJW}m2Y zwwEf2!PEqJ}0exX&`yMPTn;m#l=2kaOiuLy7 z1sqps%$tv6`{D9H!rK;q4%9=Trcfu#ju{zu=~ojI4n{QC2N&fRH%M3xw4QS)!LsG& z#=J}5&NH<%KSez!RiAsoW%?aG12f2jQ@^pXL}8{Ae<41uBe9k^`RCX5`^tZOi46NoH+0!aRfa^MR%Q|Nl>Aq52mi*^z(=+xWr3F{{k-MNEPI1zi`)SWi=+b@IE;G1dXKv zM?U7_ChKP%P*ijeh}SaK52Dv2_%_-$c__}e~hDya#}Mbx*ZBk4jx z>rrOynbTahtlHt_To@3SMp2>y7vD#NYpH=^EL-co`!LU6T}(Pos?mFYwmV)(X^~9Z6P?7U)WHDXX#lM`KKIC4s&3 z(VIJ7itkxNKq1^A3|Ep-k>_Xpknh!;9W+hjq&sd++ao2AFeJD^-~;z0Xd{WUb7!+} z%Z+Fd${EJ2os1Qc1She)j)Gp3a*-`e8#GWeYA_1a732OBXWD@*!ZgI#-qh6BG!qnW z5x0Qe6H~buE+*nFvL=xy`co7KdG^WRF6SdnJLAb~J{PbeQxbTK0x4T5ddW>58|hI= zKPd-`z_K`)vh}XD!#`QowuOQE520HX+)kgM@l_ob_NCUve%4Dd!|#-CjT@+2tLp{T zK1$qXn7_dz(-}9T_sb$d@=(%U($zYu(#*}Oanyg*VA*b2ja~0j8N;BZXX!hHN94PU zya@?;hVKmX4WINr>qaMuNUma3=wdKd*o!lq5o|~o~yD%{pb-7>p zyfVyVeME3DcWiiUd!%=`a#*AIkM)b!YlV50IK9KDy%LUF-6l5*YV{UM23%uU+n=Ql zyu7=sSmt-x|JYx==}%K+)1~09V2j}B;I-iWT^5qevDooB5{_iWsKF@0$iP^|SkJx{ zBh${)?ih0%Q~f0A$(hwNKlhp8cXNNN>>!^h-?c=>#IwfSBhRwiz1))Rhh-yW*<_n#uyz8r zowvPfm+MI$-iSP(pB||0m^)w(D~}P^omW-avR~hK?R|IHc9?2>weXmL@^Sa+uK(d0 zFp_^?_zu3UvHShd?o}FB5k8re!)zv@j|+Umf>TQ z$$ZW1nS9ICZR=J(YqtDyQu$bEiz%DQ5A5DWtqV9Uy3Ew9ht^Q8GL}Q7x=TAsv9+IT z6KYHPax>Q)RK!`iFT3fDC%$d&R30acVmkESIw|GMW>`JjxuB}C56yp+uYiCzj&Gav zNo=Eb@J`|OZH_~?3vR!85BDow=q246tSo&M3|py|v)5|rLiC5%Mb&veKuf-plaUW? zl)L9>mTEJUQ&jUOY~^+$Z^HFXRh0&>mp|gZOMX*p@LVseG!Zrm6z4apGLkl&Lj3k= zA*T1k(w$M?)84tg`CRBlu=#BBYi#AmMEyBQm>fY~_=nJ2oG+8>jZev$S29;Wn7hC% z>J#4>y*08Ia2R)VGq0;#M)@25fj)tmgjC&IEBpSnIjFgMb?e)RP5CXD{k~5%e4{@Y zPa0q>j!8geq0|qBR18%2(EX&Q#+$_z@0QxbeuXWC$I+qsll%K+b7bABkgrPox8Wb# z`lbTWBR{*mULnfsGlghl#bSlfFNkY@+I3??p1w(bhjJ%6dNb7(mw=ZYCe^>B4~wrQ zuLxZ|6Zo#ayR>{Zq;F$ictp#_*I_P<7XB51V8?WVC zCI11j=`}dMvk*$iTiRdM4x0RPRKqsFpX_wkakXgf9nRxrzSD6GMFj`2#YU~H58 z6~}dfeAUpIgv0a^7_K_MnGd-(+;f!rYsKO#ViMXQg8S5buxLZ7TB!H+Axzl$@}uhK z@`rH=hdKLa!<3I}9G4+lSC_RkHsNiNS6nA*B(&YgyI~&|MGjm#qXzVM&i}a4eE}sQ zukAz(Ji0mdEW5l@Tbk@}_<3$T03Udyq`-US@`=Ol(Ma_R=a=|z%iPQ zL_+H(*7_7k=M=)}s@1C3gR5ydJY+0reaS;>`_BrD1_Rt#TweZ;f4ugc+ySI$qy$qU zP*YPzw$MY;Jo&3U_TilrO=ip}9X-flB$I$;zLRwU14KL^9W9;LJvBlP(m|SFuZBV5 zTyg+f=GRN@hXM48#$X+w?J^Fv69b9yq-BOQl)>bkK^+Pv01f@u-PI+lh|^^(5X^K= zJ}rZ=R&W$p`}!~i>24`A^N9K__h=iGvvAT$l5OZD;%WV{i2tK zHtruD8mb=udvYpZ&o)(8y^Z-q{y;X~6iu03fU;Bc6lFz9hMIIofHJKFsL@JB>U5M^ z6Bg$}+IFh;`ZjrN|Ec_+o_|pd=P64WSW4ESHHTC$ypW;+y_Nb)j}|m}lI+@@{IQS1 zgCZ*yFe|3ea;< zEge8{qB($5W^=atDR~Ny?vB^phWJDN05nlW@u2@28>@}x{@d948UOl%fk!OruNMlJ zzMB2p=PAm^qJRAnCqu0Qb)%^Cb$}j3Cn+0#8Uki^`PX>=wxzS5z#a!@^F;V2R|&zU>Vi_MX3m8=ee-7ki2fzW?yy138vc zm@#t=#QxHg^Ir;>BihQRI*!73*?XvPAyQ1yqJxbMmg+h{YPu-3QLQ;ZaA2H6C#g8Q S3QYOT0XMIk>c74QkN6*F=2BAt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/group-border.png b/Templates/BaseGame/game/core/images/group-border.png new file mode 100644 index 0000000000000000000000000000000000000000..61234ae1fb3dee29eda38a371165d36b060ee426 GIT binary patch literal 1273 zcmVRq)0SqBM&U{ zK~RE8k;)fTv@rNVFsUphN-&N10jmTOk|LETicKV_m54TpVAK!`|L<7|`4E(7K4ARB zDus5>J(HaE?!4W7v&qH}czMI@+&44#&Ye5=o}G2_O1ZzkpGs6d@8oj17WcW!E!8%I zG7SwXm(AMBn08BdCX>0T948-RoKK$0X0t6*Q&aiH#l^fexw~LXm>2UyPMmS3yc8^% zPN&txvs=#V2iKgriKmXs^9tJFqjWWIBOj#Opr!-61IK}Xx1-s?{L4G0_&?_ilXv4x zqfZ_btu8MwSK|-wsQY~v)cnk(&+H?8J(|C{sz$fn!M^&){#~l$>@n5f-deC_;MX2J zsxZHFtA)8SUGF$_vOr$N_B~qGz~m2~oD@4WwsorNs!FYtyCThVSEZ(BX4T=g^Lm1w z!FRq%(Y|`AS3jc65Sso~f)<#1w-6X-QZF0;z1&IJw>OAO*=w$5nIM949%2g^Yzljy(oWy)Hf= zKP~7s_`nZIkS{YqF~l(je$;4#Z=xBAo(3O1_wqgCBii5tJte9r;~AA?zGq4`(Ts_n zWQL)#a^A!gm6r6qM+nP?0Sc)kNm@4Xtw-=SGYrxowl6GaQ(5@5B&1Y;X$f*x_i`5k zxlG?JMp&eYe3Cfrf#}H=#@X;~|BtO`5XzBM3_XQWv3X-F3LkUd8np$mW$;ZD@}tlb z@grOWzKMePX$!hz`6y0z95fS#p0K!L8Eqx>`_a=%75$H%65feFik{-gOB_87-@a&t zo=kbNraT|vFoAMIeOeI8_0#*bHBgfWlms3M{F=|;vk&HAk-V?7qf70j+dSQteXuCB zxme0WXRY7zZQiQU6W{Gki*a6ad*$6z80sP_3%q+`TtsI83dY1J6eRh$8vuYoz-JP{ zd_mbs-aVmUVdndNBq%XE#kqTuz$8gP5_U;MoS^NwxEOTzWZpMPmn%SJ z?(zSC4+0292nh@kvId`u$uPqZCPNED8bgi@LmbC`Ql1P|ln1?_pHA~@{Z%BXuPKfW j%4_?bDUt6}KLr>7B{DrC{G$C100000NkvXXu0mjfaJE^D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/inactive-overlay.png b/Templates/BaseGame/game/core/images/inactive-overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..feab83209cc442c5ad8dc73c2b86e0a8115c4743 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!foRZkbkkcwMLfBw9D?8~Obq{gPz9LVg>D`Q%4bP0l+XkKL$W0) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/loadingbar.png b/Templates/BaseGame/game/core/images/loadingbar.png new file mode 100644 index 0000000000000000000000000000000000000000..34f5944030dce5a174233bcef11b7d7210bbeac8 GIT binary patch literal 635 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

y=B0vFoY*eFT;806|)ERK!%dbrTa%1TK9Bjd-9 zr^}Zwi-N<44;PRUD|p8mJa{mjI(14AQ>RXy1kGcDhbtX7ZX8XXJXzkw;#?xT(6?{j z3J3uh>Ba9w{MtcKu%x6!T&t}`aS|7W2Z3^ccTP&GivWbIUcH(oO`0Ui9XxnYavbA4 z%5?&-1*M?e%a<=BO5(h$uP-kz7Yjmkmb6|ZFE2pFMP2CIHEU?vv}sa=9LG41awk#B zq7Ia{QxaviS3x(c2?E@105;E zR9{~&)}l)PttOOhZ4I|2jx8a+gcuZitEhD4$`!Ms zqC#Rpix)38j~_p7vL`X?Id;V^)8uz~lf9ey`0?YYbl|`NbLrBhsl}e~gpM6MmSaf# z4vshyr^beQs(JmIo;`aOl}3*qEj}EA0K<$KpHnmYb-2Cy`a0>A>greGimhql#EIf{ zQjw&e`c!V3Oxq|YTv72CD&=tqUmt1-QPA>^J$LS0?e%#WP%j>b2t4qxk)&k#^5x0s z71t(8Q9LZ8M*WU3_*)q908x1nc$Lp*%%DMo2E{4rrM;IrQC4khIDr8hqD>W0WMK?v!wXo*Sox6w^%mnQ(TC_-zKFZS8^{EVS zw8ffyettoOj)7h%>FMkg2ntlMeRelK&RT}s7JUfZ@!Pi-cVw4ae$%~oO))8A$|GfAL#3^{}?&;db3pOiK3Vbm7-lsLCqW$rzmzSf~tuG+U5+V zVXEU)6!6+MGU2Q63p@-Ii4Dgv|CzlIPvV?FXa!eP9ecf5D)rhdDqD7VZzm^s6_5ed zvATFL*uvwpQIcZas^e4?@Y+@sZRN44Hk?X#VqekwO{wZa04bQlkd&h8*z3(wspp$j z5&tR*cDQ0W=(-9_a6EL`lmL_h1w*(07|LK40No;%f}xJHp|J7v=~MI2p+k`!3$Zta zudn2Hr!Bl(quZU34PWn{l{=8Jow`mDubt{-#iH18 z8mX&%Art`E>G4is6t7)JdqnF zTQ{Ikk}k)E6-v_O$=*DXcE(HIdgj!iH}mZ9Z6_!B?Z}atAEOE->2fHjP?9cB#4^d& z^)Dz%_7`Wmoo_5^VDG1sH4sUD!e1e9l7Sz8obiXr@Qttc&B~q5qToJHnKFgOefF7b zTeB1JoR=+IM&-N`?Y735a_lLxXE`S3g#FBjOTps0m=oerh)ZQ9_7odyQpbo#sU70F zm=oerUUq}0W9o{4lDL?YWiHUgoDh%7mP8bdjX7E8EM3eA@u+M`1U>0uPFD038*@TD zDq9lJ#heh2Qlg7FAs&@23Fu-@h({^W#hmQEuahaoedFu>r|5?ob5cVopo=+Kw*={8 zPKZY#E|m?@q9+8;7A#mm13vml>+7N9`t|GSHykF3+Fsfd5jlj}TcX9f=zv}*>dx>;V=&NpN9e@jtbDyKdCUjy1xJXsWE znAx*ux2K+D`2Jx+ck@Rk#fmp?-lW5a54WpDwOtRsGCpq%!8$1@@9io6?0Rb#Tc7lk zCr=XojYoS*RM+E^-Kx7^y?RCS=g$}25H%UW|2(2S#h+c@o)&Yv_kRJO_n_Zf_&8bs O0000(_@_GbJ<_dZw^Q+&I7EPrEnI(eqE51YbYz7T13zwEE$$n>MSVv_3!WR xqwIV#4BRgm4JXvHMKX&S7St`!IKaTf!=UB9TBWi;$P?&J22WQ%mvv4FO#lthVzU4M literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/thumbHighlightButton.png b/Templates/BaseGame/game/core/images/thumbHighlightButton.png new file mode 100644 index 0000000000000000000000000000000000000000..9d83b75f31b965b79ff082e3a0f6e37e4c757c1b GIT binary patch literal 778 zcmV+l1NHogP)^%4Aj<$I1_}WqRRR$d z1~4y>8i*8ofd!i&&^88q;$hSbL@LC>f3!$o1Vs!3BNnyT#Q(FzWmcoc0=>O348mYE z7Di)XG#${gULYk9sZ%c;*}t7K1LT!85hAQlXK)Rq9_nInN)!ayOp3q3h5+?ZB@mfF0rU0$f5zPxzJM_-aIs(% zaacePd~O^y0Gb=Qf!I}_n|uR62^f})T}Q)U#D)ReA_iudz%LMg;eTdyg}>q%QN>TK zMd@&GqN(S^VIe239t#(mI2R5BxNsT3gC@>{!vG#!2JoSY^WiXn50?SFXyUv$4B(}@ z3n;D^SU}}Ft66XpQhe_P#$P)N-;>uu0ogpTEtG2ypD`}kb`^|A zTPQ=eg`y=V3C3g_aP84^23RtV84ZIG8wR5-l+hN-XbXi3Efk;Vaufm3qytH#c_6Z< zE(Ia9w3GDg3<4Lg-UZX@LvA#W$v3*3@#f9jjG!4Dq}d&UEfkp8X#0Q~5|tAdDOXK#LK`1pomC0NWAfc{C??-~a#s07*qo IM6N<$f>^aj6#xJL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/unavailable.png b/Templates/BaseGame/game/core/images/unavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..9d818a3765d48243dea04cf3d9a43e54b1b74bac GIT binary patch literal 26528 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/warnMat.dds b/Templates/BaseGame/game/core/images/warnMat.dds new file mode 100644 index 0000000000000000000000000000000000000000..ea99dcbd74a8b2ae49c5cff5a69743498a66e937 GIT binary patch literal 699192 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/core/images/window.png b/Templates/BaseGame/game/core/images/window.png new file mode 100644 index 0000000000000000000000000000000000000000..d9e8006e473974be6c97716e9ab9abc2e0e373e3 GIT binary patch literal 2559 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.setMaterial( "AL_ShadowVisualizeMaterial" ); + + %text = "ShadowViz"; + if ( isObject( %light ) ) + %text = %text @ " : " @ getWord( %sizeAndAspect, 0 ) @ " x " @ getWord( %sizeAndAspect, 1 ); + + AL_ShadowVizOverlayCtrl-->WindowCtrl.text = %text; +} + +/// For convenience, push the viz dialog and set the light manually from the console. +function showShadowVizForLight( %light ) +{ + if ( !AL_ShadowVizOverlayCtrl.isAwake() ) + Canvas.pushDialog( AL_ShadowVizOverlayCtrl, 100 ); + _setShadowVizLight( %light, true ); +} + +// Prevent shadowViz from changing lights in response to editor selection +// events until unlock is called. The only way a vis light will change while locked +// is if showShadowVizForLight is explicitly called by the user. +function lockShadowViz() +{ + AL_ShadowVizOverlayCtrl.islocked = true; +} + +function unlockShadowViz() +{ + AL_ShadowVizOverlayCtrl.islocked = false; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/lighting/advanced/shadowViz.gui b/Templates/BaseGame/game/core/lighting/advanced/shadowViz.gui new file mode 100644 index 000000000..05c1a8926 --- /dev/null +++ b/Templates/BaseGame/game/core/lighting/advanced/shadowViz.gui @@ -0,0 +1,78 @@ +//--------------------------------------------------------------------------------------------- +// Torque 3D +// Copyright (C) GarageGames.com, Inc. +//--------------------------------------------------------------------------------------------- + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(AL_ShadowVizOverlayCtrl) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + noCursor = true; + + new GuiWindowCtrl() { + internalName = "WindowCtrl"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "50 50"; + Extent = "347 209"; + MinExtent = "150 100"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "1"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "ShadowViz"; + closeCommand = "toggleShadowViz();"; + + new GuiMaterialCtrl() { + internalName = "MatCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "3 23"; + Extent = "341 181"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "2 2 2 2"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + materialName = "AL_ShadowVisualizeMaterial"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/core/lighting/basic/init.cs b/Templates/BaseGame/game/core/lighting/basic/init.cs new file mode 100644 index 000000000..e5fe350f5 --- /dev/null +++ b/Templates/BaseGame/game/core/lighting/basic/init.cs @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +exec( "./shadowFilter.cs" ); + +singleton GFXStateBlockData( BL_ProjectedShadowSBData ) +{ + blendDefined = true; + blendEnable = true; + blendSrc = GFXBlendDestColor; + blendDest = GFXBlendZero; + + zDefined = true; + zEnable = true; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + vertexColorEnable = true; +}; + +singleton ShaderData( BL_ProjectedShadowShaderData ) +{ + DXVertexShaderFile = "data/shaders/common/projectedShadowV.hlsl"; + DXPixelShaderFile = "data/shaders/common/projectedShadowP.hlsl"; + + OGLVertexShaderFile = "data/shaders/common/gl/projectedShadowV.glsl"; + OGLPixelShaderFile = "data/shaders/common/gl/projectedShadowP.glsl"; + + samplerNames[0] = "inputTex"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( BL_ProjectedShadowMaterial ) +{ + sampler["inputTex"] = "$miscbuff"; + + shader = BL_ProjectedShadowShaderData; + stateBlock = BL_ProjectedShadowSBData; + version = 2.0; + forwardLit = true; +}; + +function onActivateBasicLM() +{ + // If HDR is enabled... enable the special format token. + if ( $platform !$= "macos" && HDRPostFx.isEnabled ) + AL_FormatToken.enable(); + + // Create render pass for projected shadow. + new RenderPassManager( BL_ProjectedShadowRPM ); + + // Create the mesh bin and add it to the manager. + %meshBin = new RenderMeshMgr(); + BL_ProjectedShadowRPM.addManager( %meshBin ); + + // Add both to the root group so that it doesn't + // end up in the MissionCleanup instant group. + RootGroup.add( BL_ProjectedShadowRPM ); + RootGroup.add( %meshBin ); +} + +function onDeactivateBasicLM() +{ + // Delete the pass manager which also deletes the bin. + BL_ProjectedShadowRPM.delete(); +} + +function setBasicLighting() +{ + setLightManager( "Basic Lighting" ); +} diff --git a/Templates/BaseGame/game/core/lighting/basic/shadowFilter.cs b/Templates/BaseGame/game/core/lighting/basic/shadowFilter.cs new file mode 100644 index 000000000..cf2788e5f --- /dev/null +++ b/Templates/BaseGame/game/core/lighting/basic/shadowFilter.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +singleton ShaderData( BL_ShadowFilterShaderV ) +{ + DXVertexShaderFile = "data/shaders/common/lighting/basic/shadowFilterV.hlsl"; + DXPixelShaderFile = "data/shaders/common/lighting/basic/shadowFilterP.hlsl"; + + OGLVertexShaderFile = "data/shaders/common/lighting/basic/gl/shadowFilterV.glsl"; + OGLPixelShaderFile = "data/shaders/common/lighting/basic/gl/shadowFilterP.glsl"; + + samplerNames[0] = "$diffuseMap"; + + defines = "BLUR_DIR=float2(1.0,0.0)"; + + pixVersion = 2.0; +}; + +singleton ShaderData( BL_ShadowFilterShaderH : BL_ShadowFilterShaderV ) +{ + defines = "BLUR_DIR=float2(0.0,1.0)"; +}; + + +singleton GFXStateBlockData( BL_ShadowFilterSB : PFX_DefaultStateBlock ) +{ + colorWriteDefined=true; + colorWriteRed=false; + colorWriteGreen=false; + colorWriteBlue=false; + blendDefined = true; + blendEnable = true; +}; + +// NOTE: This is ONLY used in Basic Lighting, and +// only directly by the ProjectedShadow. It is not +// meant to be manually enabled like other PostEffects. +singleton PostEffect( BL_ShadowFilterPostFx ) +{ + // Blur vertically + shader = BL_ShadowFilterShaderV; + stateBlock = PFX_DefaultStateBlock; + targetClear = "PFXTargetClear_OnDraw"; + targetClearColor = "0 0 0 0"; + texture[0] = "$inTex"; + target = "$outTex"; + + // Blur horizontal + new PostEffect() + { + shader = BL_ShadowFilterShaderH; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + }; +}; diff --git a/Templates/BaseGame/game/core/lighting/shadowMaps/init.cs b/Templates/BaseGame/game/core/lighting/shadowMaps/init.cs new file mode 100644 index 000000000..eaa46cae7 --- /dev/null +++ b/Templates/BaseGame/game/core/lighting/shadowMaps/init.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +new ShaderData(BlurDepthShader) +{ + DXVertexShaderFile = "data/shaders/common/lighting/shadowMap/boxFilterV.hlsl"; + DXPixelShaderFile = "data/shaders/common/lighting/shadowMap/boxFilterP.hlsl"; + + OGLVertexShaderFile = "data/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl"; + OGLPixelShaderFile = "data/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl"; + pixVersion = 2.0; +}; diff --git a/Templates/BaseGame/game/core/main.cs b/Templates/BaseGame/game/core/main.cs new file mode 100644 index 000000000..d5c7bf45b --- /dev/null +++ b/Templates/BaseGame/game/core/main.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Initialize core sub system functionality such as audio, the Canvas, PostFX, +// rendermanager, light managers, etc. +// +// Note that not all of these need to be initialized before the client, although +// the audio should and the canvas definitely needs to be. I've put things here +// to distinguish between the purpose and functionality of the various client +// scripts. Game specific script isn't needed until we reach the shell menus +// and start a game or connect to a server. We get the various subsystems ready +// to go, and then use initClient() to handle the rest of the startup sequence. +// +// If this is too convoluted we can reduce this complexity after futher testing +// to find exactly which subsystems should be readied before kicking things off. +// ---------------------------------------------------------------------------- + +//We need to hook the missing/warn material stuff early, so do it here +$Core::MissingTexturePath = "core/images/missingTexture"; +$Core::UnAvailableTexturePath = "core/images/unavailable"; +$Core::WarningTexturePath = "core/images/warnMat"; +$Core::CommonShaderPath = "data/shaders/common"; + +/// This is the path used by ShaderGen to cache procedural +/// shaders. If left blank ShaderGen will only cache shaders +/// to memory and not to disk. +$shaderGen::cachePath = "data/shaders"; + +exec("./helperFunctions.cs"); + +// We need some of the default GUI profiles in order to get the canvas and +// other aspects of the GUI system ready. +exec("./profiles.cs"); + +//This is a bit of a shortcut, but we'll load the client's default settings to ensure all the prefs get initialized correctly +%prefPath = getPrefpath(); +if ( isFile( %prefPath @ "/clientPrefs.cs" ) ) + exec( %prefPath @ "/clientPrefs.cs" ); +else + exec("data/defaults.cs"); + +%der = $pref::Video::displayDevice; + +// Initialization of the various subsystems requires some of the preferences +// to be loaded... so do that first. +exec("./globals.cs"); + +exec("./canvas.cs"); +exec("./cursor.cs"); + +exec("./renderManager.cs"); +exec("./lighting.cs"); + +exec("./audio.cs"); +exec("./sfx/audioAmbience.cs"); +exec("./sfx/audioData.cs"); +exec("./sfx/audioDescriptions.cs"); +exec("./sfx/audioEnvironments.cs"); +exec("./sfx/audioStates.cs"); + +exec("./parseArgs.cs"); + +// Materials and Shaders for rendering various object types +exec("./gfxData/commonMaterialData.cs"); +exec("./gfxData/shaders.cs"); +exec("./gfxData/terrainBlock.cs"); +exec("./gfxData/water.cs"); +exec("./gfxData/scatterSky.cs"); +exec("./gfxData/clouds.cs"); + +// Initialize all core post effects. +exec("./postFx.cs"); + +// Seed the random number generator. +setRandomSeed(); \ No newline at end of file diff --git a/Templates/BaseGame/game/core/parseArgs.cs b/Templates/BaseGame/game/core/parseArgs.cs new file mode 100644 index 000000000..811cee00c --- /dev/null +++ b/Templates/BaseGame/game/core/parseArgs.cs @@ -0,0 +1,392 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Support functions used to manage the directory list +function pushFront(%list, %token, %delim) +{ + if (%list !$= "") + return %token @ %delim @ %list; + return %token; +} + +function pushBack(%list, %token, %delim) +{ + if (%list !$= "") + return %list @ %delim @ %token; + return %token; +} + +function popFront(%list, %delim) +{ + return nextToken(%list, unused, %delim); +} + +function parseArgs() +{ + for ($i = 1; $i < $Game::argc ; $i++) + { + $arg = $Game::argv[$i]; + $nextArg = $Game::argv[$i+1]; + $hasNextArg = $Game::argc - $i > 1; + $logModeSpecified = false; + + // Check for dedicated run + /*if( stricmp($arg,"-dedicated") == 0 ) + { + $userDirs = $defaultGame; + $dirCount = 1; + $isDedicated = true; + }*/ + + switch$ ($arg) + { + //-------------------- + case "-dedicated": + $userDirs = $defaultGame; + $dirCount = 1; + $isDedicated = true; + $Server::Dedicated = true; + enableWinConsole(true); + $argUsed[%i]++; + + //-------------------- + case "-mission": + $argUsed[%i]++; + if ($hasNextArg) + { + $missionArg = $nextArg; + $argUsed[%i+1]++; + %i++; + } + else + error("Error: Missing Command Line argument. Usage: -mission "); + + //-------------------- + case "-connect": + $argUsed[%i]++; + if ($hasNextArg) + { + $JoinGameAddress = $nextArg; + $argUsed[%i+1]++; + %i++; + } + else + error("Error: Missing Command Line argument. Usage: -connect "); + + + //-------------------- + case "-log": + $argUsed[$i]++; + if ($hasNextArg) + { + // Turn on console logging + if ($nextArg != 0) + { + // Dump existing console to logfile first. + $nextArg += 4; + } + setLogMode($nextArg); + $logModeSpecified = true; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -log "); + + //-------------------- + case "-dir": + $argUsed[$i]++; + if ($hasNextArg) + { + // Append the mod to the end of the current list + $userDirs = strreplace($userDirs, $nextArg, ""); + $userDirs = pushFront($userDirs, $nextArg, ";"); + $argUsed[$i+1]++; + $i++; + $dirCount++; + } + else + error("Error: Missing Command Line argument. Usage: -dir "); + + //-------------------- + // changed the default behavior of this command line arg. It now + // defaults to ONLY loading the game, not tools + // default auto-run already loads in tools --SRZ 11/29/07 + case "-game": + $argUsed[$i]++; + if ($hasNextArg) + { + // Set the selected dir --NOTE: we no longer allow tools with this argument + /* + if( $isDedicated ) + { + $userDirs = $nextArg; + $dirCount = 1; + } + else + { + $userDirs = "tools;" @ $nextArg; + $dirCount = 2; + } + */ + $userDirs = $nextArg; + $dirCount = 1; + $argUsed[$i+1]++; + $i++; + error($userDirs); + } + else + error("Error: Missing Command Line argument. Usage: -game "); + + //-------------------- + case "-console": + enableWinConsole(true); + $argUsed[$i]++; + + //-------------------- + case "-jSave": + $argUsed[$i]++; + if ($hasNextArg) + { + echo("Saving event log to journal: " @ $nextArg); + saveJournal($nextArg); + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -jSave "); + + //-------------------- + case "-jPlay": + $argUsed[$i]++; + if ($hasNextArg) + { + playJournal($nextArg); + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -jPlay "); + + //-------------------- + case "-jPlayToVideo": + $argUsed[$i]++; + if ($hasNextArg) + { + $VideoCapture::journalName = $nextArg; + $VideoCapture::captureFromJournal = true; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -jPlayToVideo "); + + //-------------------- + case "-vidCapFile": + $argUsed[$i]++; + if ($hasNextArg) + { + $VideoCapture::fileName = $nextArg; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -vidCapFile "); + + //-------------------- + case "-vidCapFPS": + $argUsed[$i]++; + if ($hasNextArg) + { + $VideoCapture::fps = $nextArg; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -vidCapFPS "); + + //-------------------- + case "-vidCapEncoder": + $argUsed[$i]++; + if ($hasNextArg) + { + $VideoCapture::encoder = $nextArg; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -vidCapEncoder "); + + //-------------------- + case "-vidCapWidth": + $argUsed[$i]++; + if ($hasNextArg) + { + $videoCapture::width = $nextArg; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -vidCapWidth "); + + //-------------------- + case "-vidCapHeight": + $argUsed[$i]++; + if ($hasNextArg) + { + $videoCapture::height = $nextArg; + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -vidCapHeight "); + + //-------------------- + case "-level": + $argUsed[$i]++; + if ($hasNextArg) + { + %hasExt = strpos($nextArg, ".mis"); + if(%hasExt == -1) + { + $levelToLoad = $nextArg @ " "; + + for(%i = $i + 2; %i < $Game::argc; %i++) + { + $arg = $Game::argv[%i]; + %hasExt = strpos($arg, ".mis"); + + if(%hasExt == -1) + { + $levelToLoad = $levelToLoad @ $arg @ " "; + } else + { + $levelToLoad = $levelToLoad @ $arg; + break; + } + } + } + else + { + $levelToLoad = $nextArg; + } + + $argUsed[$i+1]++; + $i++; + } + else + error("Error: Missing Command Line argument. Usage: -level "); + + //------------------- + case "-worldeditor": + $startWorldEditor = true; + $argUsed[$i]++; + + //------------------- + case "-guieditor": + $startGUIEditor = true; + $argUsed[$i]++; + + //------------------- + case "-help": + $displayHelp = true; + $argUsed[$i]++; + + //------------------- + case "-compileAll": + $compileAll = true; + $argUsed[$i]++; + + //------------------- + case "-compileTools": + $compileTools = true; + $argUsed[$i]++; + + //------------------- + case "-genScript": + $genScript = true; + $argUsed[$i]++; + + case "-fullscreen": + $cliFullscreen = true; + $argUsed[%i]++; + + case "-windowed": + $cliFullscreen = false; + $argUsed[%i]++; + + case "-openGL": + $pref::Video::displayDevice = "OpenGL"; + $argUsed[%i]++; + + case "-directX": + $pref::Video::displayDevice = "D3D"; + $argUsed[%i]++; + + case "-autoVideo": + $pref::Video::displayDevice = ""; + $argUsed[%i]++; + + case "-prefs": + $argUsed[%i]++; + if ($hasNextArg) { + exec($nextArg, true, true); + $argUsed[%i+1]++; + %i++; + } + else + error("Error: Missing Command Line argument. Usage: -prefs "); + + + //------------------- + default: + $argUsed[$i]++; + if($userDirs $= "") + $userDirs = $arg; + } + } + + //----------------------------------------------- + // Play journal to video file? + if ($VideoCapture::captureFromJournal && $VideoCapture::journalName !$= "") + { + if ($VideoCapture::fileName $= "") + $VideoCapture::fileName = $VideoCapture::journalName; + + if ($VideoCapture::encoder $= "") + $VideoCapture::encoder = "THEORA"; + + if ($VideoCapture::fps $= "") + $VideoCapture::fps = 30; + + if ($videoCapture::width $= "") + $videoCapture::width = 0; + + if ($videoCapture::height $= "") + $videoCapture::height = 0; + + playJournalToVideo( $VideoCapture::journalName, $VideoCapture::fileName, + $VideoCapture::encoder, $VideoCapture::fps, + $videoCapture::width SPC $videoCapture::height ); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/postFx.cs b/Templates/BaseGame/game/core/postFx.cs new file mode 100644 index 000000000..bb57edcb0 --- /dev/null +++ b/Templates/BaseGame/game/core/postFx.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton ShaderData( PFX_PassthruShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/passthruP.hlsl"; + +// OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; +// OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/passthruP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 2.0; +}; + +function PostEffect::inspectVars( %this ) +{ + %name = %this.getName(); + %globals = "$" @ %name @ "::*"; + inspectVars( %globals ); +} + +function PostEffect::viewDisassembly( %this ) +{ + %file = %this.dumpShaderDisassembly(); + + if ( %file $= "" ) + { + echo( "PostEffect::viewDisassembly - no shader disassembly found." ); + } + else + { + echo( "PostEffect::viewDisassembly - shader disassembly file dumped ( " @ %file @ " )." ); + openFile( %file ); + } +} + +// Return true if we really want the effect enabled. +// By default this is the case. +function PostEffect::onEnabled( %this ) +{ + return true; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/core/profiles.cs b/Templates/BaseGame/game/core/profiles.cs new file mode 100644 index 000000000..fa9d9b72a --- /dev/null +++ b/Templates/BaseGame/game/core/profiles.cs @@ -0,0 +1,226 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Set font cache path if it doesn't already exist. +if($Gui::fontCacheDirectory $= "") +{ + $Gui::fontCacheDirectory = expandFilename("./fonts"); +} + +// ---------------------------------------------------------------------------- +// GuiDefaultProfile is a special profile that all other profiles inherit +// defaults from. It must exist. +// ---------------------------------------------------------------------------- + +if(!isObject(GuiDefaultProfile)) +new GuiControlProfile (GuiDefaultProfile) +{ + tab = false; + canKeyFocus = false; + hasBitmapArray = false; + mouseOverSelected = false; + + // fill color + opaque = false; + fillColor = "242 241 240"; + fillColorHL ="228 228 235"; + fillColorSEL = "98 100 137"; + fillColorNA = "255 255 255 "; + + // border color + border = 0; + borderColor = "100 100 100"; + borderColorHL = "50 50 50 50"; + borderColorNA = "75 75 75"; + + // font + fontType = "Arial"; + fontSize = 14; + fontCharset = ANSI; + + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorNA = "0 0 0"; + fontColorSEL= "255 255 255"; + + // bitmap information + bitmap = ""; + bitmapBase = ""; + textOffset = "0 0"; + + // used by guiTextControl + modal = true; + justify = "left"; + autoSizeWidth = false; + autoSizeHeight = false; + returnTab = false; + numbersOnly = false; + cursorColor = "0 0 0 255"; +}; + +if(!isObject(GuiToolTipProfile)) +new GuiControlProfile (GuiToolTipProfile) +{ + // fill color + fillColor = "239 237 222"; + + // border color + borderColor = "138 134 122"; + + // font + fontType = "Arial"; + fontSize = 14; + fontColor = "0 0 0"; + + category = "Core"; +}; + +if(!isObject(GuiWindowProfile)) +new GuiControlProfile (GuiWindowProfile) +{ + opaque = false; + border = 2; + fillColor = "242 241 240"; + fillColorHL = "221 221 221"; + fillColorNA = "200 200 200"; + fontColor = "50 50 50"; + fontColorHL = "0 0 0"; + bevelColorHL = "255 255 255"; + bevelColorLL = "0 0 0"; + text = "untitled"; + bitmap = "./images/window"; + textOffset = "8 4"; + hasBitmapArray = true; + justify = "left"; + category = "Core"; +}; + + +if(!isObject(GuiTextEditProfile)) +new GuiControlProfile(GuiTextEditProfile) +{ + opaque = true; + bitmap = "./images/textEdit"; + hasBitmapArray = true; + border = -2; + fillColor = "242 241 240 0"; + fillColorHL = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fontColorSEL = "98 100 137"; + fontColorNA = "200 200 200"; + textOffset = "4 2"; + autoSizeWidth = false; + autoSizeHeight = true; + justify = "left"; + tab = true; + canKeyFocus = true; + category = "Core"; +}; + +if(!isObject(GuiScrollProfile)) +new GuiControlProfile(GuiScrollProfile) +{ + opaque = true; + fillcolor = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "150 150 150"; + border = true; + bitmap = "./images/scrollBar"; + hasBitmapArray = true; + category = "Core"; +}; + +if(!isObject(GuiOverlayProfile)) +new GuiControlProfile(GuiOverlayProfile) +{ + opaque = true; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fillColor = "0 0 0 100"; + category = "Core"; +}; + +if(!isObject(GuiCheckBoxProfile)) +new GuiControlProfile(GuiCheckBoxProfile) +{ + opaque = false; + fillColor = "232 232 232"; + border = false; + borderColor = "100 100 100"; + fontSize = 14; + fontColor = "20 20 20"; + fontColorHL = "80 80 80"; + fontColorNA = "200 200 200"; + fixedExtent = true; + justify = "left"; + bitmap = "./images/checkbox"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( GuiProgressProfile ) ) +new GuiControlProfile( GuiProgressProfile ) +{ + opaque = false; + fillColor = "0 162 255 200"; + border = true; + borderColor = "50 50 50 200"; + category = "Core"; +}; + +if( !isObject( GuiProgressBitmapProfile ) ) +new GuiControlProfile( GuiProgressBitmapProfile ) +{ + border = false; + hasBitmapArray = true; + bitmap = "./images/loadingbar"; + category = "Core"; +}; + +if( !isObject( GuiProgressTextProfile ) ) +new GuiControlProfile( GuiProgressTextProfile ) +{ + fontSize = "14"; + fontType = "Arial"; + fontColor = "0 0 0"; + justify = "center"; + category = "Core"; +}; + +if( !isObject( GuiButtonProfile ) ) +new GuiControlProfile( GuiButtonProfile ) +{ + opaque = true; + border = true; + + fontColor = "50 50 50"; + fontColorHL = "0 0 0"; + fontColorNA = "200 200 200"; + //fontColorSEL ="0 0 0"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "./images/button"; + hasBitmapArray = false; + category = "Core"; +}; diff --git a/Templates/BaseGame/game/core/renderManager.cs b/Templates/BaseGame/game/core/renderManager.cs new file mode 100644 index 000000000..f719ad1f0 --- /dev/null +++ b/Templates/BaseGame/game/core/renderManager.cs @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initRenderManager() +{ + assert( !isObject( DiffuseRenderPassManager ), "initRenderManager() - DiffuseRenderPassManager already initialized!" ); + + new RenderPassManager( DiffuseRenderPassManager ); + + // This token, and the associated render managers, ensure that driver MSAA + // does not get used for Advanced Lighting renders. The 'AL_FormatResolve' + // PostEffect copies the result to the backbuffer. + new RenderFormatToken(AL_FormatToken) + { + enabled = "false"; + + format = getBestHDRFormat(); + depthFormat = "GFXFormatD24S8"; + aaLevel = 0; // -1 = match backbuffer + + // The contents of the back buffer before this format token is executed + // is provided in $inTex + copyEffect = "AL_FormatCopy"; + + // The contents of the render target created by this format token is + // provided in $inTex + resolveEffect = "AL_FormatCopy"; + }; + DiffuseRenderPassManager.addManager( new RenderPassStateBin() { renderOrder = 0.001; stateToken = AL_FormatToken; } ); + + // We really need to fix the sky to render after all the + // meshes... but that causes issues in reflections. + DiffuseRenderPassManager.addManager( new RenderObjectMgr(SkyBin) { bintype = "Sky"; renderOrder = 0.1; processAddOrder = 0.1; } ); + + //DiffuseRenderPassManager.addManager( new RenderVistaMgr() { bintype = "Vista"; renderOrder = 0.15; processAddOrder = 0.15; } ); + + DiffuseRenderPassManager.addManager( new RenderObjectMgr(BeginBin) { bintype = "Begin"; renderOrder = 0.2; processAddOrder = 0.2; } ); + // Normal mesh rendering. + DiffuseRenderPassManager.addManager( new RenderTerrainMgr(TerrainBin) { renderOrder = 0.4; processAddOrder = 0.4; basicOnly = true; } ); + DiffuseRenderPassManager.addManager( new RenderMeshMgr(MeshBin) { bintype = "Mesh"; renderOrder = 0.5; processAddOrder = 0.5; basicOnly = true; } ); + DiffuseRenderPassManager.addManager( new RenderImposterMgr(ImposterBin) { renderOrder = 0.56; processAddOrder = 0.56; } ); + DiffuseRenderPassManager.addManager( new RenderObjectMgr(ObjectBin) { bintype = "Object"; renderOrder = 0.6; processAddOrder = 0.6; } ); + + DiffuseRenderPassManager.addManager( new RenderObjectMgr(ShadowBin) { bintype = "Shadow"; renderOrder = 0.7; processAddOrder = 0.7; } ); + DiffuseRenderPassManager.addManager( new RenderMeshMgr(DecalRoadBin) { bintype = "DecalRoad"; renderOrder = 0.8; processAddOrder = 0.8; } ); + DiffuseRenderPassManager.addManager( new RenderMeshMgr(DecalBin) { bintype = "Decal"; renderOrder = 0.81; processAddOrder = 0.81; } ); + DiffuseRenderPassManager.addManager( new RenderOcclusionMgr(OccluderBin){ bintype = "Occluder"; renderOrder = 0.9; processAddOrder = 0.9; } ); + + // We now render translucent objects that should handle + // their own fogging and lighting. + + // Note that the fog effect is triggered before this bin. + DiffuseRenderPassManager.addManager( new RenderObjectMgr(ObjTranslucentBin) { bintype = "ObjectTranslucent"; renderOrder = 1.0; processAddOrder = 1.0; } ); + + DiffuseRenderPassManager.addManager( new RenderObjectMgr(WaterBin) { bintype = "Water"; renderOrder = 1.2; processAddOrder = 1.2; } ); + DiffuseRenderPassManager.addManager( new RenderObjectMgr(FoliageBin) { bintype = "Foliage"; renderOrder = 1.3; processAddOrder = 1.3; } ); + DiffuseRenderPassManager.addManager( new RenderParticleMgr(ParticleBin) { renderOrder = 1.35; processAddOrder = 1.35; } ); + DiffuseRenderPassManager.addManager( new RenderTranslucentMgr(TranslucentBin){ renderOrder = 1.4; processAddOrder = 1.4; } ); + + DiffuseRenderPassManager.addManager(new RenderObjectMgr(FogBin){ bintype = "ObjectVolumetricFog"; renderOrder = 1.45; processAddOrder = 1.45; } ); + + // Note that the GlowPostFx is triggered after this bin. + DiffuseRenderPassManager.addManager( new RenderGlowMgr(GlowBin) { renderOrder = 1.5; processAddOrder = 1.5; } ); + + // We render any editor stuff from this bin. Note that the HDR is + // completed before this bin to keep editor elements from tone mapping. + DiffuseRenderPassManager.addManager( new RenderObjectMgr(EditorBin) { bintype = "Editor"; renderOrder = 1.6; processAddOrder = 1.6; } ); + + // Resolve format change token last. + DiffuseRenderPassManager.addManager( new RenderPassStateBin(FinalBin) { renderOrder = 1.7; stateToken = AL_FormatToken; } ); +} + +/// This is the Default PostFX state block. Put here to prevent any missing object +/// errors for below dependencies +singleton GFXStateBlockData( PFX_DefaultStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +/// This post effect is used to copy data from the non-MSAA back-buffer to the +/// device back buffer (which could be MSAA). It must be declared here so that +/// it is initialized when 'AL_FormatToken' is initialzed. +singleton GFXStateBlockData( AL_FormatTokenState : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; +}; + +singleton PostEffect( AL_FormatCopy ) +{ + // This PostEffect is used by 'AL_FormatToken' directly. It is never added to + // the PostEffectManager. Do not call enable() on it. + isEnabled = false; + allowReflectPass = true; + + shader = PFX_PassthruShader; + stateBlock = AL_FormatTokenState; + + texture[0] = "$inTex"; + target = "$backbuffer"; +}; diff --git a/Templates/BaseGame/game/core/sfx/audioAmbience.cs b/Templates/BaseGame/game/core/sfx/audioAmbience.cs new file mode 100644 index 000000000..8c2bf270c --- /dev/null +++ b/Templates/BaseGame/game/core/sfx/audioAmbience.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton SFXAmbience( AudioAmbienceDefault ) +{ + environment = AudioEnvOff; +}; + +singleton SFXAmbience( AudioAmbienceOutside ) +{ + environment = AudioEnvPlain; + states[ 0 ] = AudioLocationOutside; +}; + +singleton SFXAmbience( AudioAmbienceInside ) +{ + environment = AudioEnvRoom; + states[ 0 ] = AudioLocationInside; +}; + +singleton SFXAmbience( AudioAmbienceUnderwater ) +{ + environment = AudioEnvUnderwater; + states[ 0 ] = AudioLocationUnderwater; +}; diff --git a/Templates/BaseGame/game/core/sfx/audioData.cs b/Templates/BaseGame/game/core/sfx/audioData.cs new file mode 100644 index 000000000..8584ce50e --- /dev/null +++ b/Templates/BaseGame/game/core/sfx/audioData.cs @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Game specific audio descriptions. Always declare SFXDescription's (the type of sound) +// before SFXProfile's (the sound itself) when creating new ones + +singleton SFXDescription(BulletFireDesc : AudioEffect ) +{ + isLooping = false; + is3D = true; + ReferenceDistance = 10.0; + MaxDistance = 60.0; +}; + +singleton SFXDescription(BulletImpactDesc : AudioEffect ) +{ + isLooping = false; + is3D = true; + ReferenceDistance = 10.0; + MaxDistance = 30.0; + volume = 0.4; + pitch = 1.4; +}; diff --git a/Templates/BaseGame/game/core/sfx/audioDescriptions.cs b/Templates/BaseGame/game/core/sfx/audioDescriptions.cs new file mode 100644 index 000000000..d682461cf --- /dev/null +++ b/Templates/BaseGame/game/core/sfx/audioDescriptions.cs @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Always declare SFXDescription's (the type of sound) before SFXProfile's (the +// sound itself) when creating new ones + +//----------------------------------------------------------------------------- +// 3D Sounds +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Single shot sounds +//----------------------------------------------------------------------------- + +singleton SFXDescription( AudioDefault3D : AudioEffect ) +{ + is3D = true; + ReferenceDistance = 20.0; + MaxDistance = 100.0; +}; + +singleton SFXDescription( AudioSoft3D : AudioEffect ) +{ + is3D = true; + ReferenceDistance = 20.0; + MaxDistance = 100.0; + volume = 0.4; +}; + +singleton SFXDescription( AudioClose3D : AudioEffect ) +{ + is3D = true; + ReferenceDistance = 10.0; + MaxDistance = 60.0; +}; + +singleton SFXDescription( AudioClosest3D : AudioEffect ) +{ + is3D = true; + ReferenceDistance = 5.0; + MaxDistance = 10.0; +}; + +//----------------------------------------------------------------------------- +// Looping sounds +//----------------------------------------------------------------------------- + +singleton SFXDescription( AudioDefaultLoop3D : AudioEffect ) +{ + isLooping = true; + is3D = true; + ReferenceDistance = 20.0; + MaxDistance = 100.0; +}; + +singleton SFXDescription( AudioCloseLoop3D : AudioEffect ) +{ + isLooping = true; + is3D = true; + ReferenceDistance = 18.0; + MaxDistance = 25.0; +}; + +singleton SFXDescription( AudioClosestLoop3D : AudioEffect ) +{ + isLooping = true; + is3D = true; + ReferenceDistance = 5.0; + MaxDistance = 10.0; +}; + +//----------------------------------------------------------------------------- +// 2d sounds +//----------------------------------------------------------------------------- + +// Used for non-looping environmental sounds (like power on, power off) +singleton SFXDescription( Audio2D : AudioEffect ) +{ + isLooping = false; +}; + +// Used for Looping Environmental Sounds +singleton SFXDescription( AudioLoop2D : AudioEffect ) +{ + isLooping = true; +}; + +singleton SFXDescription( AudioStream2D : AudioEffect ) +{ + isStreaming = true; +}; +singleton SFXDescription( AudioStreamLoop2D : AudioEffect ) +{ + isLooping = true; + isStreaming = true; +}; + +//----------------------------------------------------------------------------- +// Music +//----------------------------------------------------------------------------- + +singleton SFXDescription( AudioMusic2D : AudioMusic ) +{ + isStreaming = true; +}; + +singleton SFXDescription( AudioMusicLoop2D : AudioMusic ) +{ + isLooping = true; + isStreaming = true; +}; + +singleton SFXDescription( AudioMusic3D : AudioMusic ) +{ + isStreaming = true; + is3D = true; +}; + +singleton SFXDescription( AudioMusicLoop3D : AudioMusic ) +{ + isStreaming = true; + is3D = true; + isLooping = true; +}; diff --git a/Templates/BaseGame/game/core/sfx/audioEnvironments.cs b/Templates/BaseGame/game/core/sfx/audioEnvironments.cs new file mode 100644 index 000000000..671825b6b --- /dev/null +++ b/Templates/BaseGame/game/core/sfx/audioEnvironments.cs @@ -0,0 +1,916 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Reverb environment presets. +// +// For customized presets, best derive from one of these presets. + +singleton SFXEnvironment( AudioEnvOff ) +{ + envSize = "7.5"; + envDiffusion = "1.0"; + room = "-10000"; + roomHF = "-10000"; + roomLF = "0"; + decayTime = "1.0"; + decayHFRatio = "1.0"; + decayLFRatio = "1.0"; + reflections = "-2602"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "200"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "0.0"; + density = "0.0"; + flags = 0x33; +}; + +singleton SFXEnvironment( AudioEnvGeneric ) +{ + envSize = "7.5"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-100"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.83"; + decayLFRatio = "1.0"; + reflections = "-2602"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "200"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvRoom ) +{ + envSize = "1.9"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-454"; + roomLF = "0"; + decayTime = "0.4"; + decayHFRatio = "0.83"; + decayLFRatio = "1.0"; + reflections = "-1646"; + reflectionsDelay = "0.002"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "53"; + reverbDelay = "0.003"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvPaddedCell ) +{ + envSize = "1.4"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-6000"; + roomLF = "0"; + decayTime = "0.17"; + decayHFRatio = "0.1"; + decayLFRatio = "1.0"; + reflections = "-1204"; + reflectionsDelay = "0.001"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "207"; + reverbDelay = "0.002"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvBathroom ) +{ + envSize = "1.4"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-1200"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.54"; + decayLFRatio = "1.0"; + reflections = "-370"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "1030"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "60.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvLivingRoom ) +{ + envSize = "2.5"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-6000"; + roomLF = "0"; + decayTime = "0.5"; + decayHFRatio = "0.1"; + decayLFRatio = "1.0"; + reflections = "-1376"; + reflectionsDelay = "0.003"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1104"; + reverbDelay = "0.004"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvStoneRoom ) +{ + envSize = "11.6"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "300"; + roomLF = "0"; + decayTime = "2.31"; + decayHFRatio = "0.64"; + decayLFRatio = "1.0"; + reflections = "-711"; + reflectionsDelay = "0.012"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "83"; + reverbDelay = "0.017"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "-5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvAuditorium ) +{ + envSize = "21.6"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-476"; + roomLF = "0"; + decayTime = "4.32"; + decayHFRatio = "0.59"; + decayLFRatio = "1.0"; + reflections = "1"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-289"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvConcertHall ) +{ + envSize = "19.6"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-500"; + roomLF = "0"; + decayTime = "3.92"; + decayHFRatio = "0.7"; + decayLFRatio = "1.0"; + reflections = "-1230"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-2"; + reverbDelay = "0.029"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvCave ) +{ + envSize = "14.6"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "0"; + roomLF = "0"; + decayTime = "2.91"; + decayHFRatio = "1.3"; + decayLFRatio = "1.0"; + reflections = "-602"; + reflectionsDelay = "0.015"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-302"; + reverbDelay = "0.022"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x1f; +}; + +singleton SFXEnvironment( AudioEnvArena ) +{ + envSize = "36.2f"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-698"; + roomLF = "0"; + decayTime = "7.24"; + decayHFRatio = "0.33"; + decayLFRatio = "1.0"; + reflections = "-1166"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "16"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvHangar ) +{ + envSize = "50.3"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-1000"; + roomLF = "0"; + decayTime = "10.05"; + decayHFRatio = "0.23"; + decayLFRatio = "1.0"; + reflections = "-602"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "198"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvCarpettedHallway ) +{ + envSize = "1.9"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-4000"; + roomLF = "0"; + decayTime = "0.3"; + decayHFRatio = "0.1"; + decayLFRatio = "1.0"; + reflections = "-1831"; + reflectionsDelay = "0.002"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1630"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvHallway ) +{ + envSize = "1.8"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-300"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.59"; + decayLFRatio = "1.0"; + reflections = "-1219"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "441"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvStoneCorridor ) +{ + envSize = "13.5"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-237"; + roomLF = "0"; + decayTime = "2.7"; + decayHFRatio = "0.79"; + decayLFRatio = "1.0"; + reflections = "-1214"; + reflectionsDelay = "0.013"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "395"; + reverbDelay = "0.02"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvAlley ) +{ + envSize = "7.5"; + envDiffusion = "0.3"; + room = "-1000"; + roomHF = "-270"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.86"; + decayLFRatio = "1.0"; + reflections = "-1204"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-4"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.125"; + echoDepth = "0.95"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvForest ) +{ + envSize = "38.0"; + envDiffusion = "0.3"; + room = "-1000"; + roomHF = "-3300"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.54"; + decayLFRatio = "1.0"; + reflections = "-2560"; + reflectionsDelay = "0.162"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-229"; + reverbDelay = "0.088"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.125"; + echoDepth = "1.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "79.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvCity ) +{ + envSize = "7.5"; + envDiffusion = "0.5"; + room = "-1000"; + roomHF = "-800"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.67"; + decayLFRatio = "1.0"; + reflections = "-2273"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1691"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "50.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvMountains ) +{ + envSize = "100.0"; + envDiffusion = "0.27"; + room = "-1000"; + roomHF = "-2500"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.21"; + decayLFRatio = "1.0"; + reflections = "-2780"; + reflectionsDelay = "0.3"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1434"; + reverbDelay = "0.1"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "1.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "27.0"; + density = "100.0"; + flags = 0x1f; +}; + +singleton SFXEnvironment( AudioEnvQuary ) +{ + envSize = "17.5"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-1000"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.83"; + decayLFRatio = "1.0"; + reflections = "-10000"; + reflectionsDelay = "0.061"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "500"; + reverbDelay = "0.025"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.125"; + echoDepth = "0.7"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvPlain ) +{ + envSize = "42.5"; + envDiffusion = "0.21"; + room = "-1000"; + roomHF = "-2000"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.5"; + decayLFRatio = "1.0"; + reflections = "-2466"; + reflectionsDelay = "0.179"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1926"; + reverbDelay = "0.1"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "1.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "21.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvParkingLot ) +{ + envSize = "8.3"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "0"; + roomLF = "0"; + decayTime = "1.65"; + decayHFRatio = "1.5"; + decayLFRatio = "1.0"; + reflections = "-1363"; + reflectionsDelay = "0.008"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-1153"; + reverbDelay = "0.012"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x1f; +}; + +singleton SFXEnvironment( AudioEnvSewerPipe ) +{ + envSize = "1.7"; + envDiffusion = "0.8"; + room = "-1000"; + roomHF = "-1000"; + roomLF = "0"; + decayTime = "2.81"; + decayHFRatio = "0.14"; + decayLFRatio = "1.0"; + reflections = "429"; + reflectionsDelay = "0.014"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "1023"; + reverbDelay = "0.21"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "0.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "80.0"; + density = "60.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvUnderwater ) +{ + envSize = "1.8"; + envDiffusion = "1.0"; + room = "-1000"; + roomHF = "-4000"; + roomLF = "0"; + decayTime = "1.49"; + decayHFRatio = "0.1"; + decayLFRatio = "1.0"; + reflections = "-449"; + reflectionsDelay = "0.007"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "1700"; + reverbDelay = "0.011"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "1.18"; + modulationDepth = "0.348"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x3f; +}; + +singleton SFXEnvironment( AudioEnvDrugged ) +{ + envSize = "1.9"; + envDiffusion = "0.5"; + room = "-1000"; + roomHF = "0"; + roomLF = "0"; + decayTime = "8.39"; + decayHFRatio = "1.39"; + decayLFRatio = "1.0"; + reflections = "-115"; + reflectionsDelay = "0.002"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "985"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "0.25"; + modulationDepth = "1.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x1f; +}; + +singleton SFXEnvironment( AudioEnvDizzy ) +{ + envSize = "1.8"; + envDiffusion = "0.6"; + room = "-1000.0"; + roomHF = "-400"; + roomLF = "0"; + decayTime = "17.23"; + decayHFRatio = "0.56"; + decayLFRatio = "1.0"; + reflections = "-1713"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "-613"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "1.0"; + modulationTime = "0.81"; + modulationDepth = "0.31"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x1f; +}; + +singleton SFXEnvironment( AudioEnvPsychotic ) +{ + envSize = "1.0"; + envDiffusion = "0.5"; + room = "-1000"; + roomHF = "-151"; + roomLF = "0"; + decayTime = "7.56"; + decayHFRatio = "0.91"; + decayLFRatio = "1.0"; + reflections = "-626"; + reflectionsDelay = "0.02"; + reflectionsPan[ 0 ] = "0.0"; + reflectionsPan[ 1 ] = "0.0"; + reflectionsPan[ 2 ] = "0.0"; + reverb = "774"; + reverbDelay = "0.03"; + reverbPan[ 0 ] = "0.0"; + reverbPan[ 1 ] = "0.0"; + reverbPan[ 2 ] = "0.0"; + echoTime = "0.25"; + echoDepth = "0.0"; + modulationTime = "4.0"; + modulationDepth = "1.0"; + airAbsorptionHF = "-5.0"; + HFReference = "5000.0"; + LFReference = "250.0"; + roomRolloffFactor = "0.0"; + diffusion = "100.0"; + density = "100.0"; + flags = 0x1f; +}; diff --git a/Templates/BaseGame/game/core/sfx/audioStates.cs b/Templates/BaseGame/game/core/sfx/audioStates.cs new file mode 100644 index 000000000..3ab55cf78 --- /dev/null +++ b/Templates/BaseGame/game/core/sfx/audioStates.cs @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Some state presets. + + +/// Return the first active SFXState in the given SimSet/SimGroup. +function sfxGetActiveStateInGroup( %group ) +{ + %count = %group.getCount(); + for( %i = 0; %i < %count; %i ++ ) + { + %obj = %group.getObject( %i ); + if( !%obj.isMemberOfClass( "SFXState" ) ) + continue; + + if( %obj.isActive() ) + return %obj; + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// Special audio state that will always and only be active when no other +// state is active. Useful for letting slots apply specifically when no +// other slot in a list applies. + +singleton SFXState( AudioStateNone ) {}; + +AudioStateNone.activate(); + +function SFXState::onActivate( %this ) +{ + if( %this.getId() != AudioStateNone.getId() ) + AudioStateNone.disable(); +} + +function SFXState::onDeactivate( %this ) +{ + if( %this.getId() != AudioStateNone.getId() ) + AudioStateNone.enable(); +} + +//----------------------------------------------------------------------------- +// AudioStateExclusive class. +// +// Automatically deactivates sibling SFXStates in its parent SimGroup +// when activated. + +function AudioStateExclusive::onActivate( %this ) +{ + Parent::onActivate( %this ); + + %group = %this.parentGroup; + %count = %group.getCount(); + + for( %i = 0; %i < %count; %i ++ ) + { + %obj = %group.getObject( %i ); + + if( %obj != %this && %obj.isMemberOfClass( "SFXState" ) && %obj.isActive() ) + %obj.deactivate(); + } +} + +//----------------------------------------------------------------------------- +// Location-dependent states. + +singleton SimGroup( AudioLocation ); + +/// State when the listener is outside. +singleton SFXState( AudioLocationOutside ) +{ + parentGroup = AudioLocation; + className = "AudioStateExclusive"; +}; + +/// State when the listener is submerged. +singleton SFXState( AudioLocationUnderwater ) +{ + parentGroup = AudioLocation; + className = "AudioStateExclusive"; +}; + +/// State when the listener is indoors. +singleton SFXState( AudioLocationInside ) +{ + parentGroup = AudioLocation; + className = "AudioStateExclusive"; +}; + +/// Return the currently active SFXState in AudioLocation. +function sfxGetLocation() +{ + return sfxGetActiveStateInGroup( AudioLocation ); +} + +//----------------------------------------------------------------------------- +// Mood-dependent states. + +singleton SimGroup( AudioMood ); + +singleton SFXState( AudioMoodNeutral ) +{ + parentGroup = AudioMood; + className = "AudioStateExclusive"; +}; + +singleton SFXState( AudioMoodAggressive ) +{ + parentGroup = AudioMood; + className = "AudioStateExclusive"; +}; + +singleton SFXState( AudioMoodTense ) +{ + parentGroup = AudioMood; + className = "AudioStateExclusive"; +}; + +singleton SFXState( AudioMoodVictory ) +{ + parentGroup = AudioMood; + className = "AudioStateExclusive"; +}; + +singleton SFXState( AudioMoodCalm ) +{ + parentGroup = AudioMood; + className = "AudioStateExclusive"; +}; + +/// Return the currently active SFXState in AudioMood. +function sfxGetMood() +{ + return sfxGetActiveStateInGroup( AudioMood ); +} diff --git a/Templates/BaseGame/game/data/clientServer/ClientServer.cs b/Templates/BaseGame/game/data/clientServer/ClientServer.cs new file mode 100644 index 000000000..3cf7aaa55 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/ClientServer.cs @@ -0,0 +1,112 @@ + +// The general flow of a gane - server's creation, loading and hosting clients, and then destruction is as follows: + +// First, a client will always create a server in the event that they want to host a single player +// game. Torque3D treats even single player connections as a soft multiplayer game, with some stuff +// in the networking short-circuited to sidestep around lag and packet transmission times. + +// initServer() is called, loading the default server scripts. +// After that, if this is a dedicated server session, initDedicated() is called, otherwise initClient is called +// to prep a playable client session. + +// When a local game is started - a listen server - via calling StartGame() a server is created and then the client is +// connected to it via createAndConnectToLocalServer(). + +function ClientServer::create( %this ) +{ + echo("\n--------- Initializing Directory: scripts ---------"); + exec( "./scripts/client/client.cs" ); + exec( "./scripts/server/server.cs" ); + + $Game::MissionGroup = "MissionGroup"; + + initServer(); + + %dbList = new ArrayObject(DatablockFilesList); + + // Start up in either client, or dedicated server mode + if ($Server::Dedicated) + { + initDedicated(); + } + else + { + initClient(); + } +} + +function ClientServer::destroy( %this ) +{ + // Ensure that we are disconnected and/or the server is destroyed. + // This prevents crashes due to the SceneGraph being deleted before + // the objects it contains. + if ($Server::Dedicated) + destroyServer(); + else + disconnect(); + + // Destroy the physics plugin. + physicsDestroy(); + + sfxShutdown(); + + echo("Exporting client prefs"); + %prefPath = getPrefpath(); + export("$pref::*", %prefPath @ "/clientPrefs.cs", false); + + echo("Exporting server prefs"); + export("$Pref::Server::*", %prefPath @ "/serverPrefs.cs", false); + BanList::Export(%prefPath @ "/banlist.cs"); +} + +//----------------------------------------------------------------------------- +function StartGame( %mission, %hostingType ) +{ + if( %mission $= "" ) + { + %id = CL_levelList.getSelectedId(); + %mission = getField(CL_levelList.getRowTextById(%id), 1); + //error("Cannot start a level with no level selected!"); + } + + if (%hostingType !$= "") + { + %serverType = %hostingType; + } + else + { + if ($pref::HostMultiPlayer) + %serverType = "MultiPlayer"; + else + %serverType = "SinglePlayer"; + } + + // Show the loading screen immediately. + if ( isObject( LoadingGui ) ) + { + Canvas.setContent("LoadingGui"); + LoadingProgress.setValue(1); + LoadingProgressTxt.setValue("LOADING MISSION FILE"); + Canvas.repaint(); + } + + createAndConnectToLocalServer( %serverType, %mission ); +} + +function JoinGame( %serverIndex ) +{ + // The server info index is stored in the row along with the + // rest of displayed info. + if( setServerInfo( %serverIndex ) ) + { + Canvas.setContent("LoadingGui"); + LoadingProgress.setValue(1); + LoadingProgressTxt.setValue("WAITING FOR SERVER"); + Canvas.repaint(); + + %conn = new GameConnection(ServerConnection); + %conn.setConnectArgs($pref::Player::Name); + %conn.setJoinPassword($Client::Password); + %conn.connect($ServerInfo::Address); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/ClientServer.module b/Templates/BaseGame/game/data/clientServer/ClientServer.module new file mode 100644 index 000000000..05c70a90d --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/ClientServer.module @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/client/client.cs b/Templates/BaseGame/game/data/clientServer/scripts/client/client.cs new file mode 100644 index 000000000..0b18d81aa --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/client/client.cs @@ -0,0 +1,29 @@ +function initClient() +{ + echo("\n--------- Initializing " @ $appName @ ": Client Scripts ---------"); + + // Make sure this variable reflects the correct state. + $Server::Dedicated = false; + + // Game information used to query the master server + $Client::GameTypeQuery = $appName; + $Client::MissionTypeQuery = "Any"; + + exec( "data/clientServer/scripts/client/message.cs" ); + exec( "data/clientServer/scripts/client/connectionToServer.cs" ); + exec( "data/clientServer/scripts/client/levelDownload.cs" ); + exec( "data/clientServer/scripts/client/levelLoad.cs" ); + + //load prefs + %prefPath = getPrefpath(); + if ( isFile( %prefPath @ "/clientPrefs.cs" ) ) + exec( %prefPath @ "/clientPrefs.cs" ); + else + exec( "data/defaults.cs" ); + + loadMaterials(); + + // Copy saved script prefs into C++ code. + setDefaultFov( $pref::Player::defaultFov ); + setZoomSpeed( $pref::Player::zoomSpeed ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/client/connectionToServer.cs b/Templates/BaseGame/game/data/clientServer/scripts/client/connectionToServer.cs new file mode 100644 index 000000000..693c7fff7 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/client/connectionToServer.cs @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Functions dealing with connecting to a server + +//---------------------------------------------------------------------------- +// GameConnection client callbacks +//---------------------------------------------------------------------------- +// Called on the new connection object after connect() succeeds. +function GameConnection::onConnectionAccepted(%this) +{ + // Startup the physX world on the client before any + // datablocks and objects are ghosted over. + physicsInitWorld( "client" ); +} + +function GameConnection::initialControlSet(%this) +{ + echo ("*** Initial Control Object"); + + // The first control object has been set by the server + // and we are now ready to go. + + // first check if the editor is active + if (!isToolBuild() || !isMethod("Editor", "checkActiveLoadDone") || !Editor::checkActiveLoadDone()) + { + if (Canvas.getContent() != PlayGui.getId()) + Canvas.setContent(PlayGui); + } +} + +function GameConnection::onControlObjectChange(%this) +{ + echo ("*** Control Object Changed"); + + // Reset the current FOV to match the new object + // and turn off any current zoom. + resetCurrentFOV(); + turnOffZoom(); +} + +function GameConnection::onConnectionError(%this, %msg) +{ + // General connection error, usually raised by ghosted objects + // initialization problems, such as missing files. We'll display + // the server's connection error message. + disconnectedCleanup(); + MessageBoxOK( "DISCONNECT", $ServerConnectionErrorMessage @ " (" @ %msg @ ")" ); +} + +//----------------------------------------------------------------------------- +// Server connection error +//----------------------------------------------------------------------------- +addMessageCallback( 'MsgConnectionError', handleConnectionErrorMessage ); + +function handleConnectionErrorMessage(%msgType, %msgString, %msgError) +{ + // On connect the server transmits a message to display if there + // are any problems with the connection. Most connection errors + // are game version differences, so hopefully the server message + // will tell us where to get the latest version of the game. + $ServerConnectionErrorMessage = %msgError; +} + +//----------------------------------------------------------------------------- +// Disconnect +//----------------------------------------------------------------------------- + +function disconnect() +{ + // We need to stop the client side simulation + // else physics resources will not cleanup properly. + physicsStopSimulation( "client" ); + + // Delete the connection if it's still there. + if (isObject(ServerConnection)) + ServerConnection.delete(); + + disconnectedCleanup(); + + // Call destroyServer in case we're hosting + destroyServer(); +} + +function disconnectedCleanup() +{ + // End mission, if it's running. + + if( $Client::missionRunning ) + clientEndMission(); + + // Disable mission lighting if it's going, this is here + // in case we're disconnected while the mission is loading. + + $lightingMission = false; + $sceneLighting::terminateLighting = true; + + // Back to the launch screen + if (isObject( MainMenuGui )) + Canvas.setContent( MainMenuGui ); + + // Before we destroy the client physics world + // make sure all ServerConnection objects are deleted. + if(isObject(ServerConnection)) + { + ServerConnection.deleteAllObjects(); + } + + // We can now delete the client physics simulation. + physicsDestroyWorld( "client" ); +} diff --git a/Templates/BaseGame/game/data/clientServer/scripts/client/levelDownload.cs b/Templates/BaseGame/game/data/clientServer/scripts/client/levelDownload.cs new file mode 100644 index 000000000..dd70e31ea --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/client/levelDownload.cs @@ -0,0 +1,185 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Mission Loading +// The client portion of the client/server mission loading process +//----------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +// Loading Phases: +// Phase 1: Transmit Datablocks +// Transmit targets +// Phase 2: Transmit Ghost Objects +// Phase 3: Start Game +// +// The server invokes the client MissionStartPhase[1-3] function to request +// permission to start each phase. When a client is ready for a phase, +// it responds with MissionStartPhase[1-3]Ack. + +//---------------------------------------------------------------------------- +// Phase 1 +//---------------------------------------------------------------------------- +function clientCmdMissionStartPhase1(%seq, %missionName) +{ + // These need to come after the cls. + echo ("*** New Mission: " @ %missionName); + echo ("*** Phase 1: Download Datablocks & Targets"); + + //Prep the postFX stuff + // Load the post effect presets for this mission. + %path = filePath( %missionName ) @ "/" @ fileBase( %missionName ) @ $PostFXManager::fileExtension; + + if ( isScriptFile( %path ) ) + { + postFXManager::loadPresetHandler( %path ); + } + else + { + PostFXManager::settingsApplyDefaultPreset(); + } + + onMissionDownloadPhase("LOADING DATABLOCKS"); + + commandToServer('MissionStartPhase1Ack', %seq); +} + +function onDataBlockObjectReceived(%index, %total) +{ + onMissionDownloadProgress(%index / %total); +} + +//---------------------------------------------------------------------------- +// Phase 2 +//---------------------------------------------------------------------------- +function clientCmdMissionStartPhase2(%seq,%missionName) +{ + onPhaseComplete(); + echo ("*** Phase 2: Download Ghost Objects"); + + onMissionDownloadPhase("LOADING OBJECTS"); + + commandToServer('MissionStartPhase2Ack', %seq); +} + +function onGhostAlwaysStarted(%ghostCount) +{ + $ghostCount = %ghostCount; + $ghostsRecvd = 0; +} + +function onGhostAlwaysObjectReceived() +{ + $ghostsRecvd++; + onMissionDownloadProgress($ghostsRecvd / $ghostCount); +} + +//---------------------------------------------------------------------------- +// Phase 3 +//---------------------------------------------------------------------------- +function clientCmdMissionStartPhase3(%seq,%missionName) +{ + onPhaseComplete(); + StartClientReplication(); + StartFoliageReplication(); + + // Load the static mission decals. + if(isFile(%missionName @ ".decals")) + decalManagerLoad( %missionName @ ".decals" ); + + echo ("*** Phase 3: Mission Lighting"); + $MSeq = %seq; + $Client::MissionFile = %missionName; + + // Need to light the mission before we are ready. + // The sceneLightingComplete function will complete the handshake + // once the scene lighting is done. + if (lightScene("sceneLightingComplete", "")) + { + echo("Lighting mission...."); + schedule(1, 0, "updateLightingProgress"); + + onMissionDownloadPhase("LIGHTING MISSION"); + + $lightingMission = true; + } +} + +function updateLightingProgress() +{ + onMissionDownloadProgress($SceneLighting::lightingProgress); + if ($lightingMission) + $lightingProgressThread = schedule(1, 0, "updateLightingProgress"); +} + +function sceneLightingComplete() +{ + echo("Mission lighting done"); + $lightingMission = false; + + onPhaseComplete("STARTING MISSION"); + + // The is also the end of the mission load cycle. + commandToServer('MissionStartPhase3Ack', $MSeq); +} + +//---------------------------------------------------------------------------- +// Helper functions +//---------------------------------------------------------------------------- +function connect(%server) +{ + %conn = new GameConnection(ServerConnection); + RootGroup.add(ServerConnection); + %conn.setConnectArgs($pref::Player::Name); + %conn.setJoinPassword($Client::Password); + %conn.connect(%server); +} + +function onMissionDownloadPhase(%phase) +{ + if ( !isObject( LoadingProgress ) ) + return; + + LoadingProgress.setValue(0); + LoadingProgressTxt.setValue(%phase); + Canvas.repaint(); +} + +function onMissionDownloadProgress(%progress) +{ + if ( !isObject( LoadingProgress ) ) + return; + + LoadingProgress.setValue(%progress); + Canvas.repaint(33); +} + +function onPhaseComplete(%text) +{ + if ( !isObject( LoadingProgress ) ) + return; + + if(%text !$= "") + LoadingProgressTxt.setValue(%text); + + LoadingProgress.setValue( 1 ); + Canvas.repaint(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/client/levelLoad.cs b/Templates/BaseGame/game/data/clientServer/scripts/client/levelLoad.cs new file mode 100644 index 000000000..c3fd04280 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/client/levelLoad.cs @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// Whether the local client is currently running a mission. +$Client::missionRunning = false; + +// Sequence number for currently running mission. +$Client::missionSeq = -1; + + +// Called when mission is started. +function clientStartMission() +{ + // The client recieves a mission start right before + // being dropped into the game. + physicsStartSimulation( "client" ); + + // Start game audio effects channels. + + AudioChannelEffects.play(); + + // Create client mission cleanup group. + + new SimGroup( ClientMissionCleanup ); + + // Done. + + $Client::missionRunning = true; +} + +// Called when mission is ended (either through disconnect or +// mission end client command). +function clientEndMission() +{ + // Stop physics simulation on client. + physicsStopSimulation( "client" ); + + // Stop game audio effects channels. + + AudioChannelEffects.stop(); + + // Delete all the decals. + decalManagerClear(); + + // Delete client mission cleanup group. + if( isObject( ClientMissionCleanup ) ) + ClientMissionCleanup.delete(); + + clearClientPaths(); + + // Done. + $Client::missionRunning = false; +} + +//---------------------------------------------------------------------------- +// Mission start / end events sent from the server +//---------------------------------------------------------------------------- + +function clientCmdMissionStart(%seq) +{ + clientStartMission(); + $Client::missionSeq = %seq; +} + +function clientCmdMissionEnd( %seq ) +{ + if( $Client::missionRunning && $Client::missionSeq == %seq ) + { + clientEndMission(); + $Client::missionSeq = -1; + } +} diff --git a/Templates/BaseGame/game/data/clientServer/scripts/client/message.cs b/Templates/BaseGame/game/data/clientServer/scripts/client/message.cs new file mode 100644 index 000000000..c532d50d9 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/client/message.cs @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Functions that process commands sent from the server. + +// Game event descriptions, which may or may not include text messages, can be +// sent using the message* functions in core/scripts/server/message.cs. Those +// functions do commandToClient with the tag ServerMessage, which invokes the +// function below. + +// For ServerMessage messages, the client can install callbacks that will be +// run, according to the "type" of the message. + +function clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10) +{ + // Get the message type; terminates at any whitespace. + %tag = getWord(%msgType, 0); + + // First see if there is a callback installed that doesn't have a type; + // if so, that callback is always executed when a message arrives. + for (%i = 0; (%func = $MSGCB["", %i]) !$= ""; %i++) { + call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10); + } + + // Next look for a callback for this particular type of ServerMessage. + if (%tag !$= "") { + for (%i = 0; (%func = $MSGCB[%tag, %i]) !$= ""; %i++) { + call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10); + } + } +} + +// Called by the client to install a callback for a particular type of +// ServerMessage. +function addMessageCallback(%msgType, %func) +{ + for (%i = 0; (%afunc = $MSGCB[%msgType, %i]) !$= ""; %i++) { + // If it already exists as a callback for this type, + // nothing to do. + if (%afunc $= %func) { + return; + } + } + // Set it up. + $MSGCB[%msgType, %i] = %func; +} + +// The following is the callback that will be executed for every ServerMessage, +// because we're going to install it without a specified type. Any type- +// specific callbacks will be executed afterward. + +// This just invokes onServerMessage, which can be overridden by the game +function onServerMessage(%a, %b, %c, %d, %e, %f, %g, %h, %i) +{ + echo("onServerMessage: "); + if(%a !$= "") echo(" +- a: " @ %a); + if(%b !$= "") echo(" +- b: " @ %b); + if(%c !$= "") echo(" +- c: " @ %c); + if(%d !$= "") echo(" +- d: " @ %d); + if(%e !$= "") echo(" +- e: " @ %e); + if(%f !$= "") echo(" +- f: " @ %f); + if(%g !$= "") echo(" +- g: " @ %g); + if(%h !$= "") echo(" +- h: " @ %h); + if(%i !$= "") echo(" +- i: " @ %i); +} + +function defaultMessageCallback(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10) +{ + onServerMessage(detag(%msgString)); +} + +// Register that default message handler now. +addMessageCallback("", defaultMessageCallback); diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/audio.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/audio.cs new file mode 100644 index 000000000..67b2fbf5e --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/audio.cs @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +function ServerPlay2D(%profile) +{ + // Play the given sound profile on every client. + // The sounds will be transmitted as an event, not attached to any object. + for(%idx = 0; %idx < ClientGroup.getCount(); %idx++) + ClientGroup.getObject(%idx).play2D(%profile); +} + +function ServerPlay3D(%profile,%transform) +{ + // Play the given sound profile at the given position on every client + // The sound will be transmitted as an event, not attached to any object. + for(%idx = 0; %idx < ClientGroup.getCount(); %idx++) + ClientGroup.getObject(%idx).play3D(%profile,%transform); +} + diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/commands.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/commands.cs new file mode 100644 index 000000000..85a08cc47 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/commands.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Misc. server commands avialable to clients +//----------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// Debug commands +//---------------------------------------------------------------------------- + +function serverCmdNetSimulateLag( %client, %msDelay, %packetLossPercent ) +{ + if ( %client.isAdmin ) + %client.setSimulatedNetParams( %packetLossPercent / 100.0, %msDelay ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/connectionToClient.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/connectionToClient.cs new file mode 100644 index 000000000..c3a4a3e26 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/connectionToClient.cs @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// This script function is called before a client connection +// is accepted. Returning "" will accept the connection, +// anything else will be sent back as an error to the client. +// All the connect args are passed also to onConnectRequest +// +function GameConnection::onConnectRequest( %client, %netAddress, %name ) +{ + echo("Connect request from: " @ %netAddress); + if($Server::PlayerCount >= $pref::Server::MaxPlayers) + return "CR_SERVERFULL"; + return ""; +} + +//----------------------------------------------------------------------------- +// This script function is the first called on a client accept +function GameConnection::onConnect( %this, %clientData ) +{ + // Send down the connection error info, the client is responsible for + // displaying this message if a connection error occurs. + messageClient(%this, 'MsgConnectionError', "", $Pref::Server::ConnectionError); + + // Send mission information to the client + sendLoadInfoToClient(%this); + + // Simulated client lag for testing... + // %client.setSimulatedNetParams(0.1, 30); + + // Get the client's unique id: + // %authInfo = %client.getAuthInfo(); + // %client.guid = getField(%authInfo, 3); + %this.guid = 0; + addToServerGuidList(%this.guid); + + // Set admin status + if (%this.getAddress() $= "local") + { + %this.isAdmin = true; + %this.isSuperAdmin = true; + } + else + { + %this.isAdmin = false; + %this.isSuperAdmin = false; + } + + echo("CADD: "@ %this @" "@ %this.getAddress()); + + // If the mission is running, go ahead download it to the client + if ($missionRunning) + { + %this.loadMission(); + } + else if ($Server::LoadFailMsg !$= "") + { + messageClient(%this, 'MsgLoadFailed', $Server::LoadFailMsg); + } + + %this.connectData = %clientData; + + $Server::PlayerCount++; +} + +//----------------------------------------------------------------------------- +// A player's name could be obtained from the auth server, but for +// now we use the one passed from the client. +// %realName = getField( %authInfo, 0 ); +// +function GameConnection::setPlayerName(%client,%name) +{ + %client.sendGuid = 0; + + // Minimum length requirements + %name = trim( strToPlayerName( %name ) ); + if ( strlen( %name ) < 3 ) + %name = "Poser"; + + // Make sure the alias is unique, we'll hit something eventually + if (!isNameUnique(%name)) + { + %isUnique = false; + for (%suffix = 1; !%isUnique; %suffix++) { + %nameTry = %name @ "." @ %suffix; + %isUnique = isNameUnique(%nameTry); + } + %name = %nameTry; + } + + // Tag the name with the "smurf" color: + %client.nameBase = %name; + %client.playerName = addTaggedString("\cp\c8" @ %name @ "\co"); +} + +function isNameUnique(%name) +{ + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %test = ClientGroup.getObject( %i ); + %rawName = stripChars( detag( getTaggedString( %test.playerName ) ), "\cp\co\c6\c7\c8\c9" ); + if ( strcmp( %name, %rawName ) == 0 ) + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// This function is called when a client drops for any reason +// +function GameConnection::onDrop(%client, %reason) +{ + if($missionRunning) + theLevelInfo.onClientLeaveGame(); + + removeFromServerGuidList( %client.guid ); + + $Server::PlayerCount--; +} + +//----------------------------------------------------------------------------- + +function GameConnection::startMission(%this) +{ + // Inform the client the mission starting + commandToClient(%this, 'MissionStart', $missionSequence); +} + + +function GameConnection::endMission(%this) +{ + // Inform the client the mission is done. Note that if this is + // called as part of the server destruction routine, the client will + // actually never see this comment since the client connection will + // be destroyed before another round of command processing occurs. + // In this case, the client will only see the disconnect from the server + // and should manually trigger a mission cleanup. + commandToClient(%this, 'MissionEnd', $missionSequence); +} diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/defaults.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/defaults.cs new file mode 100644 index 000000000..28f54b841 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/defaults.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//Firstly, set up our standard server prefs + +// List of master servers to query, each one is tried in order +// until one responds +$Pref::Server::RegionMask = 2; +$pref::Master[0] = "2:master.garagegames.com:28002"; + +// Information about the server +$Pref::Server::Name = "Torque 3D Server"; +$Pref::Server::Info = "This is a Torque 3D server."; + +// The connection error message is transmitted to the client immediatly +// on connection, if any further error occures during the connection +// process, such as network traffic mismatch, or missing files, this error +// message is display. This message should be replaced with information +// usefull to the client, such as the url or ftp address of where the +// latest version of the game can be obtained. +$Pref::Server::ConnectionError = + "You do not have the correct version of "@$appName@" or "@ + "the related art needed to play on this server, please contact "@ + "the server administrator."; + +// The network port is also defined by the client, this value +// overrides pref::net::port for dedicated servers +$Pref::Server::Port = 28000; + +// If the password is set, clients must provide it in order +// to connect to the server +$Pref::Server::Password = ""; + +// Password for admin clients +$Pref::Server::AdminPassword = ""; + +// Misc server settings. +$Pref::Server::MaxPlayers = 64; +$Pref::Server::TimeLimit = 20; // In minutes +$Pref::Server::KickBanTime = 300; // specified in seconds +$Pref::Server::BanTime = 1800; // specified in seconds +$Pref::Server::FloodProtectionEnabled = 1; +$Pref::Server::MaxChatLen = 120; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/levelDownload.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/levelDownload.cs new file mode 100644 index 000000000..a598ee04f --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/levelDownload.cs @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Mission Loading +// The server portion of the client/server mission loading process +//----------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +// Loading Phases: +// Phase 1: Transmit Datablocks +// Transmit targets +// Phase 2: Transmit Ghost Objects +// Phase 3: Start Game +// +// The server invokes the client MissionStartPhase[1-3] function to request +// permission to start each phase. When a client is ready for a phase, +// it responds with MissionStartPhase[1-3]Ack. + +//---------------------------------------------------------------------------- +// Phase 1 +//---------------------------------------------------------------------------- +function GameConnection::loadMission(%this) +{ + // Send over the information that will display the server info + // when we learn it got there, we'll send the data blocks + %this.currentPhase = 0; + if (%this.isAIControlled()) + { + // Cut to the chase... + theLevelInfo.onEnterGame(%this); + } + else + { + commandToClient(%this, 'MissionStartPhase1', $missionSequence, $Server::MissionFile); + + echo("*** Sending mission load to client: " @ $Server::MissionFile); + } +} + +function serverCmdMissionStartPhase1Ack(%client, %seq) +{ + // Make sure to ignore calls from a previous mission load + if (%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 0) + return; + + %client.currentPhase = 1; + + // Start with the CRC + %client.setMissionCRC( $missionCRC ); + + // Send over the datablocks... + // OnDataBlocksDone will get called when have confirmation + // that they've all been received. + %client.transmitDataBlocks($missionSequence); +} + +function GameConnection::onDataBlocksDone( %this, %missionSequence ) +{ + // Make sure to ignore calls from a previous mission load + if (%missionSequence != $missionSequence || %this.currentPhase != 1) + return; + + %this.currentPhase = 1.5; + + // On to the next phase + commandToClient(%this, 'MissionStartPhase2', $missionSequence, $Server::MissionFile); +} + +//---------------------------------------------------------------------------- +// Phase 2 +//---------------------------------------------------------------------------- +function serverCmdMissionStartPhase2Ack(%client, %seq) +{ + // Make sure to ignore calls from a previous mission load + if (%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 1.5) + return; + + %client.currentPhase = 2; + + // Update mod paths, this needs to get there before the objects. + %client.transmitPaths(); + + // Start ghosting objects to the client + %client.activateGhosting(); +} + +function GameConnection::clientWantsGhostAlwaysRetry(%client) +{ + if($MissionRunning) + %client.activateGhosting(); +} + +function GameConnection::onGhostAlwaysFailed(%client) +{ +} + +function GameConnection::onGhostAlwaysObjectsReceived(%client) +{ + // Ready for next phase. + commandToClient(%client, 'MissionStartPhase3', $missionSequence, $Server::MissionFile); +} + +//---------------------------------------------------------------------------- +// Phase 3 +//---------------------------------------------------------------------------- +function serverCmdMissionStartPhase3Ack(%client, %seq) +{ + // Make sure to ignore calls from a previous mission load + if(%seq != $missionSequence || !$MissionRunning || %client.currentPhase != 2) + return; + + %client.currentPhase = 3; + + // Server is ready to drop into the game + + //Have any special game-play handling here + if(theLevelInfo.isMethod("onClientEnterGame")) + { + theLevelInfo.onClientEnterGame(%client); + } + else + { + //No Game mode class for the level info, so just spawn a default camera + // Set the control object to the default camera + if (!isObject(%client.camera)) + { + if(!isObject(Observer)) + { + datablock CameraData(Observer) + { + mode = "Observer"; + }; + } + + if (isDefined("$Game::DefaultCameraClass")) + %client.camera = spawnObject("Camera", Observer); + } + + // If we have a camera then set up some properties + if (isObject(%client.camera)) + { + MissionCleanup.add( %this.camera ); + %client.camera.scopeToClient(%client); + + %client.setControlObject(%client.camera); + + %client.camera.setTransform("0 0 1 0 0 0 0"); + } + } + + %client.startMission(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/levelInfo.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/levelInfo.cs new file mode 100644 index 000000000..51df91204 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/levelInfo.cs @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Loading info is text displayed on the client side while the mission +// is being loaded. This information is extracted from the mission file +// and sent to each the client as it joins. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// clearLoadInfo +// +// Clears the mission info stored +//------------------------------------------------------------------------------ +function clearLoadInfo() +{ + if (isObject(theLevelInfo)) + theLevelInfo.delete(); +} + +//------------------------------------------------------------------------------ +// buildLoadInfo +// +// Extract the map description from the .mis file +//------------------------------------------------------------------------------ +function buildLoadInfo( %mission ) +{ + clearLoadInfo(); + + %infoObject = ""; + %file = new FileObject(); + + if ( %file.openForRead( %mission ) ) { + %inInfoBlock = false; + + while ( !%file.isEOF() ) { + %line = %file.readLine(); + %line = trim( %line ); + + if( %line $= "new ScriptObject(MissionInfo) {" ) + %inInfoBlock = true; + else if( %line $= "new LevelInfo(theLevelInfo) {" ) + %inInfoBlock = true; + else if( %inInfoBlock && %line $= "};" ) { + %inInfoBlock = false; + %infoObject = %infoObject @ %line; + break; + } + + if( %inInfoBlock ) + %infoObject = %infoObject @ %line @ " "; + } + + %file.close(); + } + else + error("Level file " @ %mission @ " not found."); + + // Will create the object "MissionInfo" + eval( %infoObject ); + %file.delete(); +} + +//------------------------------------------------------------------------------ +// dumpLoadInfo +// +// Echo the mission information to the console +//------------------------------------------------------------------------------ +function dumpLoadInfo() +{ + echo( "Level Name: " @ theLevelInfo.name ); + echo( "Level Description:" ); + + for( %i = 0; theLevelInfo.desc[%i] !$= ""; %i++ ) + echo (" " @ theLevelInfo.desc[%i]); +} + +//------------------------------------------------------------------------------ +// sendLoadInfoToClient +// +// Sends mission description to the client +//------------------------------------------------------------------------------ +function sendLoadInfoToClient( %client ) +{ + messageClient( %client, 'MsgLoadInfo', "", theLevelInfo.levelName ); + + // Send Mission Description a line at a time + for( %i = 0; theLevelInfo.desc[%i] !$= ""; %i++ ) + messageClient( %client, 'MsgLoadDescripition', "", theLevelInfo.desc[%i] ); + + messageClient( %client, 'MsgLoadInfoDone' ); +} + +// A function used in order to easily parse the MissionGroup for classes . I'm pretty +// sure at this point the function can be easily modified to search the any group as well. +function parseMissionGroup( %className, %childGroup ) +{ + if( getWordCount( %childGroup ) == 0) + %currentGroup = "MissionGroup"; + else + %currentGroup = %childGroup; + + for(%i = 0; %i < (%currentGroup).getCount(); %i++) + { + if( (%currentGroup).getObject(%i).getClassName() $= %className ) + return true; + + if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" ) + { + if( parseMissionGroup( %className, (%currentGroup).getObject(%i).getId() ) ) + return true; + } + } +} + +// +function parseMissionGroupForIds( %className, %childGroup ) +{ + if( getWordCount( %childGroup ) == 0) + %currentGroup = $Game::MissionGroup; + else + %currentGroup = %childGroup; + + for(%i = 0; %i < (%currentGroup).getCount(); %i++) + { + if( (%currentGroup).getObject(%i).getClassName() $= %className ) + %classIds = %classIds @ (%currentGroup).getObject(%i).getId() @ " "; + + if( (%currentGroup).getObject(%i).getClassName() $= "SimGroup" ) + %classIds = %classIds @ parseMissionGroupForIds( %className, (%currentGroup).getObject(%i).getId()); + } + return %classIds; +} + +function getLevelInfo( %missionFile ) +{ + clearLoadInfo(); + + %file = new FileObject(); + + %LevelInfoObject = ""; + + if ( %file.openForRead( %missionFile ) ) { + %inInfoBlock = false; + + while ( !%file.isEOF() ) { + %line = %file.readLine(); + %line = trim( %line ); + + if( %line $= "new ScriptObject(LevelInfo) {" ) + %inInfoBlock = true; + else if( %line $= "new LevelInfo(theLevelInfo) {" ) + %inInfoBlock = true; + else if( %inInfoBlock && %line $= "};" ) { + %inInfoBlock = false; + %LevelInfoObject = %LevelInfoObject @ %line; + break; + } + + if( %inInfoBlock ) + %LevelInfoObject = %LevelInfoObject @ %line @ " "; + } + + %file.close(); + } + %file.delete(); + + if( %LevelInfoObject !$= "" ) + { + %LevelInfoObject = "%LevelInfoObject = " @ %LevelInfoObject; + eval( %LevelInfoObject ); + + return %LevelInfoObject; + } + + // Didn't find our LevelInfo + return 0; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/levelLoad.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/levelLoad.cs new file mode 100644 index 000000000..92801318d --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/levelLoad.cs @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Mission Loading +// The server portion of the client/server mission loading process +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Server mission loading +//----------------------------------------------------------------------------- +// On every mission load except the first, there is a pause after +// the initial mission info is downloaded to the client. +$MissionLoadPause = 5000; + +//----------------------------------------------------------------------------- +//This is the first call made by the server to kick the loading process off +function loadMission( %missionName, %isFirstMission ) +{ + endMission(); + echo("*** LOADING MISSION: " @ %missionName); + echo("*** Stage 1 load"); + + // increment the mission sequence (used for ghost sequencing) + $missionSequence++; + $missionRunning = false; + $Server::MissionFile = %missionName; + $Server::LoadFailMsg = ""; + + // Extract mission info from the mission file, + // including the display name and stuff to send + // to the client. + buildLoadInfo( %missionName ); + + // Download mission info to the clients + %count = ClientGroup.getCount(); + for( %cl = 0; %cl < %count; %cl++ ) + { + %client = ClientGroup.getObject( %cl ); + + if (!%client.isAIControlled()) + sendLoadInfoToClient(%client); + } + + // Now that we've sent the LevelInfo to the clients + // clear it so that it won't conflict with the actual + // LevelInfo loaded in the level + clearLoadInfo(); + + // if this isn't the first mission, allow some time for the server + // to transmit information to the clients: + if( %isFirstMission || $Server::ServerType $= "SinglePlayer" ) + loadMissionStage2(); + else + schedule( $MissionLoadPause, ServerGroup, loadMissionStage2 ); +} + +//----------------------------------------------------------------------------- + +function loadMissionStage2() +{ + echo("*** Stage 2 load"); + + // Create the mission group off the ServerGroup + $instantGroup = ServerGroup; + + // Make sure the mission exists + %file = $Server::MissionFile; + + if( !isFile( %file ) ) + { + $Server::LoadFailMsg = "Could not find mission \"" @ %file @ "\""; + } + else + { + // Calculate the mission CRC. The CRC is used by the clients + // to caching mission lighting. + $missionCRC = getFileCRC( %file ); + + // Exec the mission. The MissionGroup (loaded components) is added to the ServerGroup + exec(%file); + + if( !isObject(MissionGroup) ) + { + $Server::LoadFailMsg = "No 'MissionGroup' found in mission \"" @ %file @ "\"."; + } + } + + if( $Server::LoadFailMsg !$= "" ) + { + // Inform clients that are already connected + for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + messageClient(ClientGroup.getObject(%clientIndex), 'MsgLoadFailed', $Server::LoadFailMsg); + return; + } + + // Set mission name. + if( isObject( theLevelInfo ) ) + $Server::MissionName = theLevelInfo.levelName; + + // Mission cleanup group. This is where run time components will reside. The MissionCleanup + // group will be added to the ServerGroup. + new SimGroup( MissionCleanup ); + + // Make the MissionCleanup group the place where all new objects will automatically be added. + $instantGroup = MissionCleanup; + + // Construct MOD paths + pathOnMissionLoadDone(); + + // Mission loading done... + echo("*** Mission loaded"); + + // Start all the clients in the mission + $missionRunning = true; + for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) + ClientGroup.getObject(%clientIndex).loadMission(); + + // Go ahead and launch the game + if(TheLevelInfo.isMethod("onMissionStart")) + TheLevelInfo.onMissionStart(); +} + +function endMission() +{ + if (!isObject( MissionGroup )) + return; + + echo("*** ENDING MISSION"); + + // Inform the game code we're done. + TheLevelInfo.onMissionEnded(); + + // Inform the clients + for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) { + // clear ghosts and paths from all clients + %cl = ClientGroup.getObject( %clientIndex ); + %cl.endMission(); + %cl.resetGhosting(); + %cl.clearPaths(); + } + + // Delete everything + MissionGroup.delete(); + MissionCleanup.delete(); + + clearServerPaths(); +} + +function resetMission() +{ + echo("*** MISSION RESET"); + + // Remove any temporary mission objects + MissionCleanup.delete(); + $instantGroup = ServerGroup; + new SimGroup( MissionCleanup ); + $instantGroup = MissionCleanup; + + clearServerPaths(); + // + TheLevelInfo.onMissionReset(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/message.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/message.cs new file mode 100644 index 000000000..ebb1165aa --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/message.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +function messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + commandToClient(%client, 'ServerMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); +} + +function messageAll(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + %count = ClientGroup.getCount(); + for(%cl = 0; %cl < %count; %cl++) + { + %client = ClientGroup.getObject(%cl); + messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} + +function messageAllExcept(%client, %team, %msgtype, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + //can exclude a client, a team or both. A -1 value in either field will ignore that exclusion, so + //messageAllExcept(-1, -1, $Mesblah, 'Blah!'); will message everyone (since there shouldn't be a client -1 or client on team -1). + %count = ClientGroup.getCount(); + for(%cl= 0; %cl < %count; %cl++) + { + %recipient = ClientGroup.getObject(%cl); + if((%recipient != %client) && (%recipient.team != %team)) + messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/clientServer/scripts/server/server.cs b/Templates/BaseGame/game/data/clientServer/scripts/server/server.cs new file mode 100644 index 000000000..f45c6fe67 --- /dev/null +++ b/Templates/BaseGame/game/data/clientServer/scripts/server/server.cs @@ -0,0 +1,291 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initServer() +{ + echo("\n--------- Initializing " @ $appName @ ": Server Scripts ---------"); + + //load prefs + %prefPath = getPrefpath(); + if ( isFile( %prefPath @ "/serverPrefs.cs" ) ) + exec( %prefPath @ "/serverPrefs.cs" ); + else + exec( "data/clientServer/scripts/server/defaults.cs" ); + + exec( "data/clientServer/scripts/server/audio.cs" ); + exec( "data/clientServer/scripts/server/commands.cs" ); + exec( "data/clientServer/scripts/server/message.cs" ); + exec( "data/clientServer/scripts/server/levelDownload.cs" ); + exec( "data/clientServer/scripts/server/levelLoad.cs" ); + exec( "data/clientServer/scripts/server/levelInfo.cs" ); + exec( "data/clientServer/scripts/server/connectionToClient.cs" ); + + // Server::Status is returned in the Game Info Query and represents the + // current status of the server. This string sould be very short. + $Server::Status = "Unknown"; + + // Turn on testing/debug script functions + $Server::TestCheats = false; + + // Specify where the mission files are. + $Server::MissionFileSpec = "data/levels/*.mis"; +} + +//----------------------------------------------------------------------------- +function initDedicated() +{ + enableWinConsole(true); + echo("\n--------- Starting Dedicated Server ---------"); + + // Make sure this variable reflects the correct state. + $Server::Dedicated = true; + + // The server isn't started unless a mission has been specified. + if ($missionArg !$= "") { + createServer("MultiPlayer", $missionArg); + } + else + echo("No mission specified (use -mission filename)"); +} + +/// Attempt to find an open port to initialize the server with +function portInit(%port) +{ + %failCount = 0; + while(%failCount < 10 && !setNetPort(%port)) + { + echo("Port init failed on port " @ %port @ " trying next port."); + %port++; %failCount++; + } +} + +/// Create a server of the given type, load the given level, and then +/// create a local client connection to the server. +// +/// @return true if successful. +function createAndConnectToLocalServer( %serverType, %level ) +{ + if( !createServer( %serverType, %level ) ) + return false; + + %conn = new GameConnection( ServerConnection ); + RootGroup.add( ServerConnection ); + + %conn.setConnectArgs( $pref::Player::Name ); + %conn.setJoinPassword( $Client::Password ); + + %result = %conn.connectLocal(); + if( %result !$= "" ) + { + %conn.delete(); + destroyServer(); + + return false; + } + + return true; +} + +/// Create a server with either a "SinglePlayer" or "MultiPlayer" type +/// Specify the level to load on the server +function createServer(%serverType, %level) +{ + // Increase the server session number. This is used to make sure we're + // working with the server session we think we are. + $Server::Session++; + + if (%level $= "") + { + error("createServer(): level name unspecified"); + return false; + } + + // Make sure our level name is relative so that it can send + // across the network correctly + %level = makeRelativePath(%level, getWorkingDirectory()); + + destroyServer(); + + $missionSequence = 0; + $Server::PlayerCount = 0; + $Server::ServerType = %serverType; + $Server::LoadFailMsg = ""; + $Physics::isSinglePlayer = true; + + // Setup for multi-player, the network must have been + // initialized before now. + if (%serverType $= "MultiPlayer") + { + $Physics::isSinglePlayer = false; + + echo("Starting multiplayer mode"); + + // Make sure the network port is set to the correct pref. + portInit($Pref::Server::Port); + allowConnections(true); + + if ($pref::Net::DisplayOnMaster !$= "Never" ) + schedule(0,0,startHeartbeat); + } + + // Let the game initialize some things now that the + // the server has been created + onServerCreated(); + + loadMission(%level, true); + + $Game::running = true; + + return true; +} + +function onServerCreated() +{ + // Server::GameType is sent to the master server. + // This variable should uniquely identify your game and/or mod. + $Server::GameType = $appName; + + // Server::MissionType sent to the master server. Clients can + // filter servers based on mission type. + // $Server::MissionType = "Deathmatch"; + + // GameStartTime is the sim time the game started. Used to calculated + // game elapsed time. + $Game::StartTime = 0; + + // Create the server physics world. + physicsInitWorld( "server" ); + + physicsStartSimulation("server"); + + %cnt = DatablockFilesList.count(); + + loadDatablockFiles( DatablockFilesList, true ); + + %cnt = DatablockFilesList.count(); + + // Keep track of when the game started + $Game::StartTime = $Sim::Time; +} + +/// Shut down the server +function destroyServer() +{ + $Server::ServerType = ""; + $Server::Running = false; + + allowConnections(false); + stopHeartbeat(); + $missionRunning = false; + + // End any running levels and shut down the physics sim + onServerDestroyed(); + + physicsDestroy(); + + // Delete all the server objects + if (isObject(ServerGroup)) + ServerGroup.delete(); + + // Delete all the connections: + while (ClientGroup.getCount()) + { + %client = ClientGroup.getObject(0); + %client.delete(); + } + + $Server::GuidList = ""; + + // Delete all the data blocks... + deleteDataBlocks(); + + // Save any server settings + echo( "Exporting server prefs..." ); + export( "$Pref::Server::*", "data/clientServer/scripts/server/prefs.cs", false ); + + // Increase the server session number. This is used to make sure we're + // working with the server session we think we are. + $Server::Session++; +} + +function onServerDestroyed() +{ + physicsStopSimulation("server"); + + if (!isObject( MissionGroup )) + return; + + echo("*** ENDING MISSION"); + + // Inform the game code we're done. + if(TheLevelInfo.isMethod("onMissionEnded")) + TheLevelInfo.onMissionEnded(); + + // Inform the clients + for( %clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++ ) { + // clear ghosts and paths from all clients + %cl = ClientGroup.getObject( %clientIndex ); + %cl.endMission(); + %cl.resetGhosting(); + %cl.clearPaths(); + } + + // Delete everything + MissionGroup.delete(); + MissionCleanup.delete(); + + clearServerPaths(); +} + +/// Guid list maintenance functions +function addToServerGuidList( %guid ) +{ + %count = getFieldCount( $Server::GuidList ); + for ( %i = 0; %i < %count; %i++ ) + { + if ( getField( $Server::GuidList, %i ) == %guid ) + return; + } + + $Server::GuidList = $Server::GuidList $= "" ? %guid : $Server::GuidList TAB %guid; +} + +function removeFromServerGuidList( %guid ) +{ + %count = getFieldCount( $Server::GuidList ); + for ( %i = 0; %i < %count; %i++ ) + { + if ( getField( $Server::GuidList, %i ) == %guid ) + { + $Server::GuidList = removeField( $Server::GuidList, %i ); + return; + } + } +} + +/// When the server is queried for information, the value of this function is +/// returned as the status field of the query packet. This information is +/// accessible as the ServerInfo::State variable. +function onServerInfoQuery() +{ + return "Doing Ok"; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/defaults.cs b/Templates/BaseGame/game/data/defaults.cs new file mode 100644 index 000000000..ea7ee6a19 --- /dev/null +++ b/Templates/BaseGame/game/data/defaults.cs @@ -0,0 +1,218 @@ +$pref::Player::Name = "Visitor"; +$pref::Player::defaultFov = 75; +$pref::Player::zoomSpeed = 0; + +$pref::Net::LagThreshold = 400; +$pref::Net::Port = 28000; + +$pref::HudMessageLogSize = 40; +$pref::ChatHudLength = 1; + +$pref::Input::LinkMouseSensitivity = 1; +// DInput keyboard, mouse, and joystick prefs +$pref::Input::KeyboardEnabled = 1; +$pref::Input::MouseEnabled = 1; +$pref::Input::JoystickEnabled = 0; +$pref::Input::KeyboardTurnSpeed = 0.1; +$pref::Input::invertVerticalMouse = false; +$pref::Input::VertMouseSensitivity = 1; +$pref::Input::HorzMouseSensitivity = 1; +$pref::Input::RollMouseSensitivity = 1; +$pref::Input::ZoomVertMouseSensitivity = 0.3; +$pref::Input::ZoomHorzMouseSensitivity = 0.3; + +$sceneLighting::cacheSize = 20000; +$sceneLighting::purgeMethod = "lastCreated"; +$sceneLighting::cacheLighting = 1; + +$pref::Video::displayDevice = "D3D9"; +$pref::Video::disableVerticalSync = 1; +$pref::Video::Resolution = "1024 768"; +$pref::Video::FullScreen = false; +$pref::Video::BitDepth = "32"; +$pref::Video::RefreshRate = "60"; +$pref::Video::AA = "4"; +$pref::Video::defaultFenceCount = 0; +$pref::Video::screenShotSession = 0; +$pref::Video::screenShotFormat = "PNG"; + +/// This disables the hardware FSAA/MSAA so that +/// we depend completely on the FXAA post effect +/// which works on all cards and in deferred mode. +/// +/// Note the new Intel Hybrid graphics on laptops +/// will fail to initialize when hardware AA is +/// enabled... so you've been warned. +/// +$pref::Video::disableHardwareAA = true; + +$pref::Video::disableNormalmapping = false; + +$pref::Video::disablePixSpecular = false; + +$pref::Video::disableCubemapping = false; + +/// +$pref::Video::disableParallaxMapping = false; + +$pref::Video::Gamma = 2.2; +$pref::Video::Contrast = 1.0; +$pref::Video::Brightness = 0; + +/// The perfered light manager to use at startup. If blank +/// or if the selected one doesn't work on this platfom it +/// will try the defaults below. +$pref::lightManager = ""; + +/// This is the default list of light managers ordered from +/// most to least desirable for initialization. +$lightManager::defaults = "Advanced Lighting"; + +/// A scale to apply to the camera view distance +/// typically used for tuning performance. +$pref::camera::distanceScale = 1.0; + +/// Causes the system to do a one time autodetect +/// of an SFX provider and device at startup if the +/// provider is unset. +$pref::SFX::autoDetect = true; + +/// The sound provider to select at startup. Typically +/// this is DirectSound, OpenAL, or XACT. There is also +/// a special Null provider which acts normally, but +/// plays no sound. +$pref::SFX::provider = ""; + +/// The sound device to select from the provider. Each +/// provider may have several different devices. +$pref::SFX::device = "OpenAL"; + +/// If true the device will try to use hardware buffers +/// and sound mixing. If not it will use software. +$pref::SFX::useHardware = false; + +/// If you have a software device you have a +/// choice of how many software buffers to +/// allow at any one time. More buffers cost +/// more CPU time to process and mix. +$pref::SFX::maxSoftwareBuffers = 16; + +/// This is the playback frequency for the primary +/// sound buffer used for mixing. Although most +/// providers will reformat on the fly, for best +/// quality and performance match your sound files +/// to this setting. +$pref::SFX::frequency = 44100; + +/// This is the playback bitrate for the primary +/// sound buffer used for mixing. Although most +/// providers will reformat on the fly, for best +/// quality and performance match your sound files +/// to this setting. +$pref::SFX::bitrate = 32; + +/// The overall system volume at startup. Note that +/// you can only scale volume down, volume does not +/// get louder than 1. +$pref::SFX::masterVolume = 0.8; + +/// The startup sound channel volumes. These are +/// used to control the overall volume of different +/// classes of sounds. +$pref::SFX::channelVolume1 = 1; +$pref::SFX::channelVolume2 = 1; +$pref::SFX::channelVolume3 = 1; +$pref::SFX::channelVolume4 = 1; +$pref::SFX::channelVolume5 = 1; +$pref::SFX::channelVolume6 = 1; +$pref::SFX::channelVolume7 = 1; +$pref::SFX::channelVolume8 = 1; + +$pref::SFX::channelVolume[1] = 1; +$pref::SFX::channelVolume[2] = 1; +$pref::SFX::channelVolume[3] = 1; +$pref::SFX::channelVolume[4] = 1; + +$pref::PostEffect::PreferedHDRFormat = "GFXFormatR8G8B8A8"; + +/// This is an scalar which can be used to reduce the +/// reflection textures on all objects to save fillrate. +$pref::Reflect::refractTexScale = 1.0; + +/// This is the total frame in milliseconds to budget for +/// reflection rendering. If your CPU bound and have alot +/// of smaller reflection surfaces try reducing this time. +$pref::Reflect::frameLimitMS = 10; + +/// Set true to force all water objects to use static cubemap reflections. +$pref::Water::disableTrueReflections = false; + +// A global LOD scalar which can reduce the overall density of placed GroundCover. +$pref::GroundCover::densityScale = 1.0; + +/// An overall scaler on the lod switching between DTS models. +/// Smaller numbers makes the lod switch sooner. +$pref::TS::detailAdjust = 1.0; + +/// +$pref::Decals::enabled = true; + +/// +$pref::Decals::lifeTimeScale = "1"; + +/// The number of mipmap levels to drop on loaded textures +/// to reduce video memory usage. +/// +/// It will skip any textures that have been defined as not +/// allowing down scaling. +/// +$pref::Video::textureReductionLevel = 0; + +/// +$pref::Shadows::textureScalar = 1.0; + +/// +$pref::Shadows::disable = false; + +/// Sets the shadow filtering mode. +/// +/// None - Disables filtering. +/// +/// SoftShadow - Does a simple soft shadow +/// +/// SoftShadowHighQuality +/// +$pref::Shadows::filterMode = "SoftShadow"; + +/// +$pref::Video::defaultAnisotropy = 1; + +/// Radius in meters around the camera that ForestItems are affected by wind. +/// Note that a very large number with a large number of items is not cheap. +$pref::windEffectRadius = 25; + +/// AutoDetect graphics quality levels the next startup. +$pref::Video::autoDetect = 1; + +$PostFXManager::Settings::EnableDOF = "0"; +$PostFXManager::Settings::DOF::BlurCurveFar = ""; +$PostFXManager::Settings::DOF::BlurCurveNear = ""; +$PostFXManager::Settings::DOF::BlurMax = ""; +$PostFXManager::Settings::DOF::BlurMin = ""; +$PostFXManager::Settings::DOF::EnableAutoFocus = ""; +$PostFXManager::Settings::DOF::EnableDOF = ""; +$PostFXManager::Settings::DOF::FocusRangeMax = ""; +$PostFXManager::Settings::DOF::FocusRangeMin = ""; + +$PostFXManager::Settings::EnableLightRays = "0"; +$PostFXManager::Settings::LightRays::brightScalar = "0.75"; +$PostFXManager::Settings::LightRays::decay = "1.0"; +$PostFXManager::Settings::LightRays::density = "0.94"; +$PostFXManager::Settings::LightRays::numSamples = "40"; +$PostFXManager::Settings::LightRays::weight = "5.65"; + +$PostFXManager::Settings::EnableDOF = 1; +$pref::PostFX::EnableVignette = 1; +$pref::PostFX::EnableLightRays = 1; +$pref::PostFX::EnableHDR = 1; +$pref::PostFX::EnableSSAO = 1; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/art/AreaMap33.dds b/Templates/BaseGame/game/data/postFX/art/AreaMap33.dds new file mode 100644 index 0000000000000000000000000000000000000000..e01982a94528594a375ae6e61c44780a8e1c0b68 GIT binary patch literal 109028 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 RBF+E+002ovPDHLkV1nj@91s8i literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/postFX/art/caustics_2.png b/Templates/BaseGame/game/data/postFX/art/caustics_2.png new file mode 100644 index 0000000000000000000000000000000000000000..99097fa0ed112c28958363f2951478a0955218ec GIT binary patch literal 33963 zcmXt91yGw^(+y53?yfEF?$Dy8g%+3M!L7Kv7cIrzrC6ZAi$fsA-5r9v75D$?H}i+d z1Tq8M+}(Tk>^Wz@e^F7u!XU>0fk0Rv6lK*wAOzrB1Q0qZ@S^WrW&ymQntoJ}1-<{B*J+^mJH=Z0)c2jA7tNYcrK2# zpW1?TQ{H+T>If3!6Vtt)H{*UsobAdZM{S;@binkkX7PQ?*DS}=_aD3F>&8Z8hY`-k&jPI zObj`_d$F{%guQ%LQ3-#^&B?(Y>lr)yw*7GD>wi|~ak@$+etLJ;oSmKBu)Dh(V!IJ= z=OQ5~`CngO-}U3;vctD;Oa7fS{@1s+^Vbi4KK}mHIUxb(k+Vv3;dAJ7qld2U?t;D? z#L@2V?z1YNKQApT=w_;v!fcP-TdyxKrv)>WmyX@-8Vmde-amVgH zj>@;*-rm0hV7bb3v&VP_wvC^cbEKrCXl6Yna~tyz0nM%&|jn;46ZC%aK@xu zB68PoPQPcytSOgHb}8Ih*$Ny|kH`56l2Jo@_vo^?Fx z?p!plRK~~0A75U&ay!^zx@}%#Hj7E$*B#ZJU0t-tV2=z8d{{cYF(^$W3#_ZF6U-fx zVc^ak1J>f_lz09(Vlg#3I*L6@Wb5JWy_l1eW4Ih(_|=VjLGQ4`G536>)ni{|L*ViH z_^apGt((dCCC`Ak-b{fB9&3V3eXS$I-AhxtFuD$@ovV0<-^2BWo_ni-n1~3OA<`Vq zSQOGT5JOHbuBFY*%^*5x_&98*?cINp6J2EBH%{3JBrZAH5T3=N+pjkrXOGDW^#gZ) zXJls?P4cAd9i=nqfj9l8wN?*L&rozI_h0Jx7?8r3lfXK<$e6(AxVdbsy67(pUCq0) zmWg9H^!;}6?cAVr1)m{>ox7O_hUGsI&{(sfUBMXg^e2C*Hfo>QeOGVCLLey@x3|rK zJvlx(ak$O)4p(}ED~B+#Zd~XN#2NQ_vyPoO`o9&2h%ti?Gt2U$*rc5_3VLn%%M!zq zB%M+UYUFq|PH~bEJ^?FSBqSs-iOpRg8E5ru8yg#8KEH!74yX$>c3VrNCRR1^jx2Q6#9eb?oiad8Q@>XFPxJ$*(sB;M)c&)zMw^Aa?qQ-uW0x zHCq*6?}WEzVqU{t>Qws+%2pTmFFO4m+;N{>ls4PiUuIg3Jr);#GUKV=lpj(TbrB7s@K=oO%!V#A&pDNKs=sJ3d?WU zAM`0v%z88hA5rsuWtcSLp72THC4i|(YLBFy1XJI$*m#tjZzqHJa*C-2hn&RVe z7o*{vK0H2(%f3ExoAx}jh@1=>#;rk^_@X0(6Y-l;z7?sDG}4=$(kd15*VgHTteCT{ z)_Wq9T+Fa;kb01sO#_Uq>`leDqj9 zf=>AHJF6(({9NlxTXj14djaiZ%$!~){Mg`LICm@pHa4*g+%gZB`%^zG8)p}R104}* z+_~0xl|`dWxK`u7TJ;q)zT^*nxISJw z^Wp+dgF|CouU=u`hpj6Hw$;~Hyh$P$PYG-Akabk+`S_k~$E*v>YiD0ae*GO=^=Ta! zwb1GVO#`!^>qrv(sSp}3$!~@lh&2w8T9o7ASi_KkO#Z(e5}*rG?wY?ojUO6{FuC@X z%E<38Y*k99+?|}99Dl%BNTPd;t&A}}kBteNhNFJ7m4;JdanJvHFcHjbpO>*|+r99z zLSNQhR|KKrVuv#5Xdv>hqzT7o&ouJMN+BhBo4mM+`mLwWGe}BF;RwkO2_*DIzP|)+ zkDZIV8U*3pow+L7ngqPaoIXf#ZzSXexQ@=m^}9TMeeI%qHsp$sKZkK$ec@c`hn5>! z^NG&uTa3_yZkibi36JylsB2V>VO`rAC^KhbM~x8?5$-N7d>X`GwAW5=(3bI(iJCIH zR0>Jnwpz)@F5TVT)u1ik0k^k(gC5qesE(Wx7NFX2y@N7W&f(zOFB|UnhS`Cf2)7j+ z4i!bQk*98Ed}D=3oh{g_9p3nT#$}mCYQp7KLUMf)-X6~o4$+x6%V4ZkfY;Mv+}zfg zuY05@$@$2})|PNqravRU@<%2tu z1y>nhJ3FOK7Y1$O4r8z#gt74h96RvrG>MLoxQm2lc$Rr*)A$OwEjR;!t&pj1T{;dw zBQ$(XgsF^ha&i(rmPN+eUTvev^zbReR2W8)>>zUB+~CSsZzT#>vY>q1rO3OkSix4e za3m*(oUm^vXbW6eNY98!wZI>Gsh}HYmR46h5;LSoCAMujnoXEPAATnruh$h=*dv*1 zW>=_`Lqpfl|3P0dgaUbN3=$&BHszF%%2L)N#E*!;7iuuTES%(f=EZk`aZ^YXI)502n{V9i;JFWRhaTbz9|qU`x{rVB2>d)d;QBE zb#x4Zq(^w3iz^~H-xqnb4+zFndI`nE@4Js5`>OSRoSW9a4?zo5lKt-C;Q>UwVZ}j6 z7GcGdwS@?Idj_g|<5$zmmTn{wax%3V**9( zq`K~je%T8%bK5JMCk#_KBQ)xuhbwg`1U7aGiJT!IGWezdT1Hr-*$(NrFI4^8b?DuA1Gns+|vrcle(U zHYVi9)KFcew9Dicb9tAYKXMs$VBXk>X8+e?>V#`e=RE!}+T0@g`j{oM0QBUGO}VSa zL-F^{cNIvW#hDpR04p7DY;3?+FPfKuL@vu4b_4Z%r}`S0#7oULX%Q2NR{bEE{Qhas?&zj^{& zqf=+Q;}2C?S22+r_gDq2A2g_4KsF6UM^@cr5r(nY{qKCKC{?qwvg%t~Td%i=Qjx$& z&{&!*J?o}1yj(DLxx(0Nrn<3(EN?MY0=)tqaMF)=c6I`jMJD7a#_0{xO8YreE1MoS z`X!W>Wc_>0CF?CM@}-Dt6q+WGN?upWgYsTU>67DcyEgp-Gk5ut&bT1mIHsW@>0@Hb z8~$0R!wcLc|GLOdgTdV;}Z{w0ZC_q1!jlb$|5 zH1Bp`U;sWtROt&8AZ|oX=S+~qG*&!QS8IjaAxxR8bF|wOYpl65^FpR1+orrgnveq7;I+d1K+^-~Pk4aI zbfKOwL<~FT!8e&XZ!UV04}^^a4`^=XFPE(0DN9nVE8)M)pL~E7mI)ALB6{d zI^KqF?~EoWm;9Z%c(S3=mBwcEbP{wj)8EFOv)P0&LkO*Mo8?GPZcE^`Fz|3Na?030 z`EDwDQs*2mGfh5omr?9wWDPpzdSFAv4e-bPxn;CoS zFfKE3o>`PqfB!ZNU7?@7wQg&{$6VZhB@GC(3`_K@_m7Yg@e@OfIg+r2g)2)?HIkFh zfm~ZS<*|l(U=)HQoeyD98KSBMCLvClEHaT+=@Qq!1bKS6L6Q$jI^XPz=K`(1T6`<= zN!A~aBe*Wcw?CV58mMPohwBpj%a*v@f8T(E1Xr1Ln6KdzB@=U&l=OcsRIQm>y6}F< zdtuv17aYnmm$CD~kz3-b@szN@5W-Y4L`y<`@2G;&GN*9JSFCrZ+|97lGtN{DE93%| zo|%)rBF1D_o(c;CBO@BhzYBJ=Dggeq6QrOg7Jkgk%rsI5RR^^$I&!(X(hysN*o!vM z(WT}ekU-N~TG&_>UheLZGqh)@2l=bmp~KE=)D^slvy>j4rdzl9pPC{)pi(Xy461V( zDWOz)$1#l`vnla79y-5rb;PT{_;`EU0DT_NYvrPlbZB2&K_Ws~PJ}CN z9}lluBmg*pOj{IQnSKkvU2bwFpjLrKcF)8vnkFJleq$*hQU0w+Rkz{LruWW#Gh}VB zN=}CC83Z(u3(CaF)O}@$a$fv)rmkGhw5L0QuG24f$mx29kr-ZyorsC#<(zQ4i~D%=SkyJwi{D2gLuaXL6D z845Xz+>H``E4_V*7j#55V@Zg_@eAA5dUsEy+DFwA2|jJJaFY?4wU7m=1q#6e>Xj0l zI9w&JI^t9d2BC%qJ`qcM>+#omD7czv>uSlYQu!IE!b-K1-Rt`e$LV4!OnULNxLima zok*2-(%{-h$zl3w6ChHTmqX*%8%}(BF zP&nhLDBFxc-7MbElY3ZQOKIYLR9Phv100n9>1TQHr1qLvfvW|TH6Hel{Mq?GI5;?h z1)f0cN=r-84%ebn^!v+2>{F&WeRm0#(TeykH*X?R!No}V zGd@@JzZsvG*H6D2G=+>U@Ael8%aNYI=FSZAE+ z&z48}bco&Rak`K{kqyNa21ec=(qk?6@hpPU=c@L|Yz~A45lN%ZHR_4kAO;^eRwUgM zNbbpMh*_=U>Lj|>T|#h|?b?{*H~WLOlapBb|KyboyVU9 zC+B((!&{?!W)Z~ABnA9laX|Zv4*qIcN ziOGUv{B*pSQ)O7LJe5-x`c9dKn?174lw?M*rMM9Pu-}c;4E*>kT9SE@(idu>TAfVy z+`dlMPVOau=;+nyzG;ru##D~CT|(Y#i(JCLzwau_JRz?!2XHOiBh0+Wq^5UYX`FX7 zV|uLO+W)ho?wy|o4-3f7V~Dw|1T1Fqkb;D`I1uwRWZz6CZvJp?T%vMCyw(%Xp6}E7 zxS}4iKj5bgd)d2dxpg=LGwSx(PwHm3sQq>xn-a91C5G>|c5%ejmhs*3U-ie_fAzi_ z#Ei!9=XcLF-^vFo4TI^)=Lq02ZSwa7bGy|b$M~>n~2zsGT2&6{Ow#9Kbh1|XU znNYpeJrOiOA8Ln-@v=IyFr_?zua=dmy_ns%inBtRVB`FzeuI&-_H1ipWMuq%S>cfG zFVfR}blE-)^<}y_Lbjn1wug;0=I$pyccXX?iECB`^81RN-q6<5d5|nL(IKw?w+zjX zi=nb#bd%Q#1ga$xQYBj{R9ift&!MU{S z5waH#g;DVGcjtrh6L?7Z=4wold*QG8KCMuT>FS*`G^XH5h{b{&Z?lD>LTMWIK@%Z* zzM(IBq%njx8l-%6HDK6F|J2Q3zyz)e=e{{OyYPDtHm=+#WwN2q9T-`y1>fLL%OAHI z%#_25Kx9p=U-0U7C7ChX_eLBLEk?>;bVmqb?d4->{Uv&ce#b4s#T&2G1ymWwxr2nf ziv={>CBHdp(M}@d*o`c+*EJ4Tl&j69TdrF}s(0!Bs@$g=G+_mfhIxKV5WB8<-SJW> z6>(^eLhpdI>*dunhiTd(@v|1hkj1D}5!Kv)=<62t=s&l zd+SpJ2`Iwwt4Ok`p2#C>YjHDUptQY`2~wU6&)riYizrdO+i>WT;eLFkC-08FxnaJH zJyp%UUf`4$i=c4=F~YM&-2x^p=ixA)BRXZEz(wlNcF^N>+S69cC%zC|&E(%EMeRw~ z9Is>{vF>qxSh2m~7I{{IbV7F;x?YQ~`dub9Z!kglqA3~^S}qjWRD50)mBg9be-kWS z0}I^GnnO-4T124gU1LhrFM1(xWQi;x_Yhg9Z(k;7Y_JtP#a+R*I`zlyOW9=b$(G?l zg~upN-)$L4{oX-RWlrL+BD=nnXK$4ufpj^T5|b`j$y|xQo zD^S`X*6?H$^OkGf!IhCh&o+x>T+R9)e%`q5v}Y#?N4=kPd2$mst1n6ew@$xrUjo;S76+t*T9aF6l z6A0j6cSkN3YF3ZblmmMr<8(CbYDTC2nwfpQkNTF5%xECX=!gxK-;BA`nRdfto(#2W z13f)G+!0h#GNRDUbgv`)F_HLQeFfj=9K1-(KtzAZI1iw;GSrIjViS zc0;yLzKlFpYi|sX#u%SxxcsQtP(AjmZ%v=D*%sNZlJvr{JC_9phy{|u=)3#3|>HIrJnoCKlT=C%Cfs`Ojk<9#WaIQ9wC;Kzy?n>2*)H(~tZ z+Q|Be7?8w^wn3A~vYGZXk?!_eKFgNmrvGZqO2A4}oa7_&k*$7(8?99hol3#xZaWw_ zi^+!tC=qzV0DUh0>Km;+`}*8Fy&p6E73EUNKPJM`;>g8wk*o-; zh&|qSrn7E)Kb#_7)15|`Ac;gyNn%#Q6+!Z=BkV`Y0-`%Vu1e5El>T$!O~4aobgL*S zI{Jc32OcxYb{jKSeJ)ZHb(yUybxzDL#H0my#{U3TA_9j7yVr1Sd709aUWXgC=$7&P zT~(ausT@c*TWaxB26@vOTiGnQq8Ym_sx(@>(EY8b3_B=7T0>tuREw$=xG8)R3ACYxrutWSf+F{9)8 zlT7*5d)%TZMeSb+6KKZ2@lruNRc1h!FHOr%!Ir`OJC4}G_lpXJ1~%(ZDj#GYJ3#RM zLl^(ejh^l9q%I^v^7UaIb0CqQPtIos>6j=FlNJZ(B(w&K9}EbdFSCn*uW8BmW-2Q3 zUA_gze*DgVy8Ez^l}~}E&9QccsgpETF`-qHj$qL4Fqg2fjc^Hyqn`Tofuv0PuLgld z-1h`%wYm$I50@xshooM!$x5f$ z%DER-kZ~D@7rKief{;^SSaOkwBKUA01M9HrG70lg9%XF-L{-%CR<3k>CkRa*$(`J2 zBo-)Ax5dL-J&Z}BsOEQm9e&1ZkMp{J5>Ze5%k$ccPv(fl@lYyPNQAJMm@V)FsZTMb z+X}a?9-Q3{71=_{BVQ8xhng-!4`s(M2e4?tr)26w@klY*lQS^}cuay1b_XSnG0^(~ z^ehk_L(KZUiLi=WX~Cj5rex(u=@fILy@3qNwQs7Fk`aw*C>*NA_ixH!cwR4EAdUMt zwzm7o2S`LzyAbY1=R<2}9poiK>Tw z8$C1vMV$89hm$K)H6hiOd(BrUI)T-Z1;r4xv{36c;H*?UnHu;r$yCZIwp*a+g1*y$ zmxq5j%+>km{H511B44Pbm1$#w4@Abi`(92G9oybR6(#=0z3s}1Sl>*8w;(2^U59>^ z4xT|`TSgs=4tqe{Qil}F(<%uR{xsQInWts3X6Man`PRU`SI#6p#4C0QCiVBdEewGy z6C~;K)N!-MU%09ChO;cJ#}m3Fy+f-Vxbu~y?Op%_WZaYgnpVXOZb^1-E{^pdhenFP zE-M#H%w8L68fBQ~h#S_!m9)HfL>y09y(UxbadoD3$70eJW|6nQ9K28|sN$Cxxo~R0 z0&dOjF8LD)NfKu2$1 zXEL}>ujxFxr{L+tqmzvT;In4FpfmwWha`MuWhFWjM$e>T-)K6l?@yXsu#C=(D#j3+ zih~t^$|%t+Xpb!dh;cZfdJN#7VWSR;j1*rKGGohH@qE7cV7a%h1WgMIBT}L?A(34D zGK=_Zx!D}2iE1g(=OGWTqOQKA^zs#!dZcFI2O~uOq?%bQmP-V?PWGV-)ZADyoZ-Bj z;T(T^-uWiaLHO(sNEQ9H1mcbbMMU@?ic2UFK26B#Fm_oH;w6_CyvuKQ(o>b)3{=E^ z(6kY^L9eh`5E(%|3IPOBfC--}nX+!}?3DUKAPNX;((h{C7gDC2B3$7A9?9`LzIh0$ zfDisB!R^UDGIz{#_1LoO=XDBO>Lz#PsK5nnHRA_=fBUsVp*fzW-t~*GW8Amr z=;N|f`^~aKVhTch4^lyF-Wq!>T179@wBlftWKAysRyc?(;kV404|`Vqz;SU2UQ96{gre;O4=B1g zpdc?TEv-{;UN~#heKSNeKFFK4QZ8_qQKiA`oRP zz|HN-^iqgm&+|zQGcWj7+xCU@?KXYj8jw$Dp)^1q#nor38?^?9`JmTYt*)Lb>;Rj_VPIPPhW6ow9SN%1p^C9A7*9J>LyNWi75Zy3jpS%Iv7FRTC+B%lj$(9m&EvXE+(te_unrq zHCb(YYWLD~&!kloriNj*#;^|{Ty ziJ#mBqKWyZ8KBtcV&y#;jhK*vCfc(pzdnq0Ytg0y9}HlFuL1Te=;ozV>S;f$Wz4u6ig__mx^7IjckM$+1twwnpFvkF|O|(-oh~Nj&TY zh9gxESQ4(+VG#&d3T|&(s{uTSG`sPp&_nAmrP?wF&qoI$^?V+Wmic_KI|c~1|GnHv zy;uSoojsuM0U!8!GhT{1Ml4(bi2&D}9-FT4*&d67k6)zqJ491KE!=qePvM#v06GOv zmCUh;MlI0WHk zz;9f13`AR#R5?)b2dQ7lKqA}CPD8;|dNcrVH&j)BR4oxVJE0)oB$%jM^n>66n_ zU~#rm&$nRU2y-#l4=1421bvC^c@H8lFA0p&W3I5P#M|3%yLvOJf!EZBF_lDH0TH|B zG}2B|YHL7t*J^hR59C2eRpSIK6fQ06e_`tiJ)u+; z1fIWEhrYiy@X7u-(?%nUlJxX12`%cZ0Y&Hd{=Vf2@^aM~UK7x6h;vYThWZIQj+;Re zn`|A}bTQUE`<0Amrv<>W{Wo-Lp8J_!_1^;wohVgZ{UHCjqXY8 zU^ov63*M}(7%ZmdFMgM>E7qPl82BFFyhg^UM#UZc5C_?IUXb*?b(GGxEcoJHRoUwG z1MzZvD#on)tyDIJhnt%~AhGe^r^k!2|1IoxGae8MO8lN5z511d4y44z>w(C-2L!wn z;Gw+GyAE9*Ps40KTAs?APe4hbeO1={n4NzkX#LcjTiV-SN5A{1hZ<3!$lm)z zjv6;5ALe^P=O@21MwX9F{1&*zXu1Nc{ot9;JU+I6*e}fu7soN79aO7+7(QQKDD0xO@*3DD7q%r8 z$UGa-fE>=E8btJ^gi^wL%x8u{P;}XKsn`B3zbC%>_$#XllLwh)P<)6V`-QKAVP{di zJO|gFa7?9!&PV&R!cE4w%~1o5Y$bDPNVOVK6ZZl&1esHLmJ9rDRy_U*l~`hcMDujm z`3%V4?!abw3qQ85oXGJhy$StgC#piw6jM~M-Dpxi$O-OlUxh|0QR#SLW`u~w`p5F> z^k(u9UYCdt>^J5ZK_1vLK6K6PO@d-ET;W!(kpiyQVkD)RYks4!0N2>G8m1GyX zFM$2S5a6?_a~IEF&sMW}hFOty5*O?QW%&muPQ!M~LkX)SCKFZzNi0gKkr1^}7c6cy zLOHCWy-*J>9#jf4e|m9R*q)al@_irjdwA*q>gjcA=c9Sq)#=THyN{1p=u5RfBqLbq z=a{@sw$v1_nv_ZG zZDbLQ{aQNEiKt@vk$&q-4W6gBcIUCu_*Xw^zmlF&qrJACW!PjD--`{Z|7T`UPi7__ z&A#mZiEb6#BU^=!mJ55C^`}BSAL8o7Ot}Eg(aPEy4M_9o&Ihw~kDJe2U97VpB!3_J zwO}pzixhzm{EP_7jFf~OG7($lcQ#yP#&viJVwF)}D|izr8E1!qqrg{f%X@I?I%%ca z0m<^A-Ca1FFzhLf2-!PqS{DNKlCiHHyQ7&)=hU797BB#>A4zM?S&M$3)?k3g;&f63 zq1e!Wj_+Dmh@g3`QvUZ-{Q8dbTXXyUi>xM#trhRhY}PwJt#a^}xVPDt&C*ZJx=gl` zrg&;v&F{3Sx@Aw=T<}-%tQINSXI&`C+vQfg<_UabW3+kCCQTtAzcCRe7@jt8SmFQ=F$_q0&mf$7*fkU&dPR&_X29eA^in~ zvx^|l?o7b1qPCJ3C%>wNlZxTl!ANv~7kt}NKO&R~8*i0Vt|hEAWG9xn)B7Mm{@mK} z#Qx&Js?^@#3?4Kl1e`KKFsPf!&JMsGp4})p$s_mRTM(?&aN6yN*tbQptDNmml>&eQ ztsAG?FO*aGu~HFVRjW|0QWhc3e=vIBebU58%qmlc(73w1a}F9yVjK=E$b1ZfzfvuZ z!O+)DUeyrtuJV?uj$uKVTW8OzsvY+~auhS?|@Yco%>N*KNp`Ny%< z{F)*#+w8?lBoLYDL}7$YrFq%SJ=~lck&;kfRVC=`7_g~3d0UsbpxVnerQ zSQUrv9QikSGS~7(EiqBYqs14bFGL+Xk^Lk5LgiVKmbf@M-*3cY7o-a#&s*ooA)?$$ z2wu2~+5>@&1mz+0tr+(G_{Pfs7fdWcSWu3-1)W&f5aKEmMxlmtrVd>LRu7UpDM6~g zf-9gl|2W;=e9@eECCnH;hGp(qcIfxHou5B_Aj}y3W}80dRfeMdD+zdD-puAac~G-I z>iFNBxV;|T&+o7D)x+v*KYlG-vFvGIvlB#9rSdNgrL8=am9aDs)@Irts>Fef7e(!0YBz-1wv#|R)du8)qvxzmkbcf ze9l+W^qI%*cDgJX_vE!BH6UF=Tmk8;a7W>q0k3DEeMB#Wf}lt)EJ*_*l=mWLIz5`M zn+}4RA#Evcx4J%%-7p5HAK<}|_KvF@V@txHmcbRQ6RRJo*v!vuq8*d&&m=dQrNx9A zHlWyo4mOG~rmt`wlU_&m29B%UuwVX3WX8gVwjio0b8cdITc;(3dplB8v0t>+3x5=k1n) z4+;)oXYxKQf%$3S$5C`yX|ODQ6sBA;VVazS?#b~z6Fp8fmc?TudX=h)@onOjhMW)guS|xOU}givs8i22EDV|VNh^LZ-1)df>FtTv zV~#^E>$Nb4$JSHD%t4-acf<>T&(Ypen=7%t>&@?NYi`Qwn*zMT-;84DLf|M73EpK{ zBwinh)@w07aMHV=zOnUiA6AxBEOWTpTVCj>4i1UqE~@|5hhxkInOg-r=K!9Q?--WM z;HXyw=np~c0WVKCNRgq@O|7jyUE=pr=aKzyMftc)qvSZc7OvQWn93gl?k}ws(V+8S z!<(zCtLT9?@`FRr=Uq9^ZXBNwzzYY!b!?@mHj96*=6o%7W6V~F+TmFDmf`%VT4tom z$)w)14>dbq?id;jBl~Gq?;pbjI5I)6jh(ROz>^c4Emk&FzN&ROZ1Y={^RH)) zuA^Wg?LM42TFFYE2xqHAEYB!Ij)UmGX`+x8uFPiIlWHO_yD;FM0X75J7BMK;ZUo`4 z+}q9=&g^3G!5Hwu9cAxQ*%GAHOj%Us9Fjfo~y0jg9-CY<$DdeFa5*2={ zwsBUUmmEyQeONEhGK}NBt7)p;nfQ@b1_{1mEbKIAG?=znBGCBmNw~1JulV!^GVIvVO_c57C} zA#1knhBSezzBCQlX|F_e&9sUUZ(_<=<*ij9HfjzQ2)TXVcj|7t*U<{FY*?%+@1={z z`4Bt9cE_M}#vt!AA603Mo6$jkJM{Y1xO+)V1Z1@aU5qt8=PDa6UDiKR5gOzHbfMdk z!L=xH$g7BOa&ViQJq&vvfAPHSei?EF$aSbo7Rf znU=cvO;0Gq&{ce5x%<^yD6YP~K4m@W!MaldsG>vkjTbvL?m(iY5V;+ToGRa`q&$V{ z;bFhJm*T6J*9Gy^{#Y+pe%Z5~A3X{p*-US;b8@IrQYf7<^S)xz`kOZV?~lY`nGAE055}%~}T=vyQ>oveA<)LsSqFI_#Q_ zpnhh@cTBm};bx(DLT;dNz;rfeC?(UmQd1cqQ4QSro2OW4R-PjzQ@7?E$W#yrSPFuA zh)r<7Y}}f?7WFAgE9MDbw9=ig96X<`%!E5j5BZF-K~szGF!ZXlkY1?KKAjVmsx5Dp zHaS~_W;$v7aAW_w`(5OW*i(G!WXs|&AQgDP=A}l zKWK$`h+B8i-Fta^?WK94`<~G`6&n&)jqlBF$UtH`b4V&E4R2ng`Er}TNBY)Pz5xF9s!@{KjYCq!_g`s5hACMQ)@-X zXGjrYm{PivV<_#27+p$LN1Q{F~OVqB>RRjBFNTD4Zi^}nX#4e2s1ktXP>zB|La!kRrR zn7M*=@FN2b#RoYPhWDeFh?BJ-a%vxps0^4<%VlfJcV^Wi)F zM~mI81+4ksu16rjueY|$ezeYVg)U9FX4&ZxoVw`0z5oRxFn@HG%*8RzBgfyxEq{!( zv2N7U8VOtns8u7Zz`xOMQ%(0Q-AX$4Z4#%wO;0m0gS)>!(_5NnSiXM_|cN z-{fQceHsIbsC-;tYuFsVsB`rZbSq;Tz0*5A#Y{>`YsR6D2mlD%Sastkb9(B8zA;9j zEB?18Z4(S2CR8=tYyQ$kY&2;?LBVM$)E5y8(t+2wKVbB2+|IO(k&(E#j3x}70Wbb( zcm4qZQkR%D$Ajz0(7PvxaL0<$Lo?hh=rYg__wZRL9v zB2Wx{ij@vafi8!tp`l^8DQlESdkF?ES#2Y!1>Xcx{_H2ASKS)e|D47?o}O0!L|qv3 zmsip%Jq9*e9?q9Qvran1>r&2!M=`wpIB{`%276$*?CaG3wkv4R_(5M*t*X$n{ErIG z7!~dDS5oz$J?pg#0b^3MkFFtB;jkBAaK-;_I~5q~0Gd0mS{0kL-k%OxA(8Z$k6D>p z!W@q3VRfu?wi+oFP)BnK#uHk{rm@1tyIsIG_0JLt@bX;`fZ-Pq2phX_q3FviEC0rk zxRVfnNfY;WTD-SsB-`1kkt-r3>6Nb(?NgDt-wKxyM*8vLn_N|u@2!EQu|`YOp!Ljh zGd;!u|D#(aWybLHQozHve{Nvux7Fud0e3^uwr=k;z!Ielx#oY}&>!wx01c)y&6H4} ziz-u1j@GOG89X}prez_WGmByl`42@UOOA4!lZkk>l29H7tp@*#cc3v2^7nC9N_@@7 z*u5e4&;#sF^1#R_%3mj1!g#{Ci>E_0xd+{nBdyL@c;XxeBygD~VJ^pf#a$WFBSl81?bHnX}KaKhmg+WC|ayzbVmQ zAv6MH7hsc2%P`?3h=j-)gUaN>{L)5rRaq0Ed^2-~Zv!KYTYB6I3mVcRzJ5wLby3W{ zx)h`SLkj21T#Rm8Q*PIc61*Vxj1jKD*Dl8b_vYKa8u0>z%ZJWy0EAulktfPRN9t#8 zAUh%WoisoG+7Zw*)D*-fY<%>j zYqarj+W8F_)*@y6w0?g2@_dSR7?Gf(iTuQI%aA}EqU3!cxxbGhI&AXkIVYK{&Y_PV zu`G#D|J8Syk!@!YfSV6wZ$*ZXVU=xp+kF1{pi}Mj2MLb#`iTDh!=3Tw1^r6^fV(~8 zeya2aN~Uk`HjEE6KZ;DJo%{B&&%k}+_Siqx@m3>`Kkl8qY)U;Bnx~i=88@+eFh3Kc zNp8kBSd#^#S@b*Yj{Z`s!wdS>UqI3n$EJ=KHXe`kE%_u2jEk743?t>1o( zZ%Amca`^$*9?`A^)y#=DxAMDAcAQEKMfnS?OMgc(4)WxrrX(Jkb21G!DVaL~bJ_ZQ zua~}fnk8Vc3eblBS9$K|UXItEs{n&%V{X86vwmz@=tv*6;PJw^>M}4Rzy;a{!yX$E z+l;~x%s65a`rx`QF$m0x-7fpwW1l#|Xs#S&iC$$_^cm7z;H3_kKlBAWDpc^C z0|P1JkLn*pev=1m2dSu#viH~q-of3ng9$1~%o7gM7%O7aljkXug!Oc z<}HZhwvJE$R1leZ)b7~-ge?oFy*ScmcEbmt=m27?2YBq~$6YBJOzxgdfIg4{jBdfYSUl?NJgA;pC4V~$c6hfoirprkt00Dznn|it>SEAjC-;VK`hZ(^>9u+ zmI*=*L*z|LILP*|0oROE`)VU@)Yto?b2R5oXAw z;S9(TSbp|@fJj*l%pP3tO1*d)KQ#9}Jv|}B>bbq9&+Z0aGWv+)wPFA8C0cU5jaQ$I z5@ldRyA?>T&N0bsD-qlm$3Z4am|Iz1#jxnd;*idv|*IQ8FrNM>k#_ym33IzVJp3e-F@UiB;`=B zgjzkzf*8~kK#mBvuwIiWW@pwUY^tf(`uCMa&@iKF`O%0FSb*Iiz=+j4c0P=_b}f&FINCp6NrLaN{)0E%q-x zpt6SP%qq}Na)btGoAo#wv~zCJJ3bfQttL0{FTKTCta97Vg%Q{Fcp|i=d)I8P*GV97Ofs*qD9iN1GY9F+;RNm`MV#GOh5#zfu4zdV^lp!n2PjAjJ2x9cev> zMBt;zi&Ot;%pcn@gFY)`vq`tN^y8$bYi!zOzP$0d8#h7FZeaQ2vd9J4g;VUpKf_ig zP~G5!d|S9!Rs(o=p{#TjLK7@4-v%UoIOzWDZ^sjjlu?#gZRW>BJ#-pEmP~6&v84WJF7d{4r18PDZDht+*wV znTvbMTv%ukI%j_?!v^lFHg0@Yq$=u#edvBSk8lqiYlpOxxi?cal}Oo+-$Yt9b@XXf zVc@E)UY|+dE?7d|_!Z}vyNI3Oy4D(rfOAnWI{`3f32>-iK8Is>23tol1x1Wc#SZTc zN?_*A;T>WJ5a!3d`^)AT?t+|5M!bR!Pa6=A85Ls-M~Z|E$;d&=o#jC$f`g7Nbl$XY zIy<9`&pIFVNhvJ+YSDU5$M0sATsbwKIam?W15qeID(%1f^Mv|mwr8;|14}KDMs4G5 zFfeg2K~oM_(y}rim))-4taz)c5O#z-SM{!HVt8xjDeeFEfcqqEu8uKP*Om$)gxjty z?ChMqqNaipa4~1FjPsx}KnEbIgwEGC)5QPk2K_S(*Yxr-|D zbo4Ki`CPtbq<^|y;I%#!0vDeSw#oQ64;5a}jF7-YQJrv)g`6Spw*_v)AUTo-(Wl;= zD79i!)vKdqR}Pyw4>Xn}wKs{Wyw-eJ zaQSlu5yI*p6+a^RK%loi*ni+v+?Dst&C0_3Lo&PJfeJe9p)&LyPgkQjzcecRwkNI) z5sMp2qg3NdPVVb!uGMQFjGsN9DRa5W0x!^QX)_GTzpLPD*E$%qix_;je{B2QuKu{5 zusU(U8{1u*T*QvyxEcTj@!FWVo0X6^6~h8pJX0k|KnO98qcYzN{wU z4ah?HGRT)RXHKAn;K|^RI1OM)aOV+r>q3;aePJ92;yd+>Vd9*D$DJM@)z)Nc@VN9t3kFVP>P|0vGzwVuP< zI2SKYu7iD6KB00O5na4`Y+UTd{9Duz-(zDbXGiD9YG^GXoMz}P^t5`4&r@QiycncG& z=Wi)a{WD#`d+8c~!rq!kXb33<^#HHCB!#5m6 zk!ahh2=CvnpI0eL=ea&5U3K6Q9*+cVfD*FwVVeoVNZ}p*mXeSp9CKCk=FU}YPcstK zp!nvUE7S3%_TDTN_Gmgj3uqoj*W7IoY(8RIHrqldg;p=~jj&T2o(~T3$Jok9;XJ&5 zySJ6|wy3rGSAQPXrqSE_*gMKXsS$I$&4^t41O_TKXlc2ZLN*9->6y*U(B5yzMDv`g z5Pvbf@G0P#{j0K9bLb@-7I<*C_jSUxV1LVJy)o5d)_E-@?EEG*XI63UlPQGrGx;aa zn5|A1uQCsp2DY}XRcmD0H(_M6ScaU&$26nc*QO4mj4{Rt^`CZ$rbI$H+G3eG8ZSqr zi_Mm45D9vNOQu!yVnrw^{w&{1OBRKBYr~IEQ{VvMpNPl+?j7!}chAH8cB9kJq`Z3Z0H(Nd)qVpx12$Kg&3zcP;`f*GcRM-@J05)cm-<`0dkM^5!Bu$GgbX zkWp+!f>-39H3X8W*4tVbO3H%gz#YTCI$-UYz9+FNWaZXxB(D|3`)8Mb*tP_PKHV(h zZzX;(Pd^VdDM6T*-9yKB#F-%$ORq$<^S%KOnbO5ac<#FuT}y6YhRfBnChvVz2X#~Q z(z@8>u0ldj8=o+n`7l$=qorUMs-w@OGhZNYnCUhp+bksmrnljk5)riYyiC)NLdmTv zj2je*sOl~HryyNVQUsiFti#8$sszq0O*{R$3Duz*OghaN9=x|GQMCM4YHo4e?V|Fzt}_u6WiD57y`zPUg*$b?%Vf9a#)pa{kuFSJdcsJUBR8(2rn4LT26=y16g9%Ir7G69`Wa_FOIfaC+>q zFTbd)1bH)~*w3->q}@SY0Gsf-6tXRFcDjZ|m9C{zWKPur{{kMJ9;)7DHMz z)YO2tMEKrtyOy7HwR10e!WyUwQ^7a7GbW!K_(@b!iQ~O zw5twGTc*{W4!W}ItAjH?zum`Kp_3*?{%Heg{f%pG#V{cM;3r^X z*(Vf^6q2NK;Qg9uxlHwZ#J=V1zV@(w<>VzHh%5_8D1W=Vr2y@Vqy|SfQhDsq36kn}H{{NG336p*-X3armj) z+V5z9@!rw#39_(?I>JpqJdj5Sgqd@Aq!It@FZD)O$C5UBVk4Q$n4*HhQe_f3Y!dxL=aWEY zfF}JHILJkz$3bBAqM=wKLFw2RQ=LV01F?Ds(Znk4SMSR$wECIws8fnlqW?p=Y-8YI z5bP``E^*oY^2_F0tWw_qxg2oJa)6^@5ct)Y(42i;5NF)vjtiOw*c!q?U}338zm_ty zVA9QU(Po1LYx+*@zlUCB3Btq{{3G(Yqk6VC#k(fE({N+CVx#|p!DoET=IqgA5*oeb zHTHUSfO-EC){`D0+Pk{~d7H4Ra(S8uA?8cQ!AlHC`3lK#(MVSTI6N;1igvfvM-Ebo zPT3m=pa<|$(g#jHK>Ej$+)^-y5AX3c%u#eu!U?CBddTE{eo05_zxv~LebL;{P3qKw z=t9jY5>MU6<1bc4u_PcB04SPc5Vfh-d9>^dvt7&8X)A0E-2!Pn-szG1V_7fzW6$O; z_M>}Oy8I!nb=YtTZt8U$5hcER18G|qlP4VbRX{E;Y!td{2qf{al$c_Uc=4k=uO?v2 zhhB9pDwJge+Z@ycLjK%S>hSa!Gii7T@hq=_r}NcQ3TlYuVP_bQ9dPQtzrTNhE&-=Y zAYu4&$OW|$8niX{3z`{BPMs&?^l}NXg9NR$&+U0ZEhWdk@?2TzBEC`eq3^?N4+5b#*RnQK!LeVhUy zLqJ5eln1f=n;MFlfy-$e2d6z;DL1`TfN%rhFl5@%G&rGBOU(5j`M^1rnG=wE_)F*Z%A>ZZf(kkQS zq{Jd1#{x`;|A1%;0Gl$f6rm%(efc|Ut;3d8LQsi?!j)s=% zHczMA)U7VmGdRB4Vxle`GOtDw8-pOLKeLt|0q;f5eL+N2iUN(=8&A?-_z`{45XeCP zR(j;khm)5qWA@Z4&hCN+wkJGbdk$-guEMkxu1Gl|ra?JWl9@Elg2ekI`#s)|y_J^J z6yW8AubNlCK(HiAdP1Qw(8S`CSA~DJj7Ax*SoMIQ-NQ6!_j~BD7PoxI_>P9eQ>AQW zy5>%je=Q|^Vkx8A`rc&c}zHnw%to-(PsR;oT&ASLlM9;TBp0#riW0%C5t+<5E z^^pl=^hMvWbw6h^!9R;$fSfe*M(kW&t$GY!KwGEA`|K?*b`0?>C-7OkWR?L-d;##v z<5>-ZZw;Q#z9R*IdD+2B_rH33ApATd=<-D zqlTzplM#vkx`aWtWMgUSRRs-b7l0it0Fl+w>5`WuGC&A>8uY(qL#t*6yWDhAJ&nB- zdCs;z1B>_wh!`M=1X)cCT}}1(G=Dp^`S^gbdqA7`>LW2O^;C6r7vYW)tFf~9IMd&Q zl!e052A6ZJ(rZDac&wE^P$ff+8Mh7&M)Wq2)dh6uJ)br2b|+16b!4b9;YH~aYm~bd z$@JG^6<49JXy#0<8-ME@B%YDsNQIXgD3V5?n|mM*T{Ad1X!P+PP)}N*dOAgGVg=kE z0HKrn5^U{Mx55;R!K}=4(F}KVNI;cjTo*^qJ`|kpa5$P}6jmX?EkNuIu9}{ro~Up! z62umXCK2Jf{;P@RAsH6^aRdpGtprI^Y%P===GSBLp{_Xqk*tLmh#A%MP47!itL6FWKIn{A2Fd@N z&z~y^489I*-yrBz0LT8hu^HTj5&5(%N9bqg4XuL^a9vfPRg;{nOb0jK3JhH!%<^=X zFZ6p96woY1(G_-ES9{RJY%gj{YpVmm1klsdqp;PmCHZArG`pG=!!+Z_Xhx0Rlst*0`hNHOCBo} zLqDNTfSM&CfCsPBhj(5~V#)4}ug3lNx-ltzaDZJEKe=Wi%WX>!$oT>P*l9<)bW&g6 z8Kfw$SNYxa!kVE5&fpvvp}D`*_vZTkG2X>ECV|!W>7fCTXlelflqW0NTMh(=8|4Iey6!7#$d19$mn%<4 zE7+dh*0rP^PXM1SKn~NS?H*|~>gvzuoVa(>kjam{()g&5i$bL7O@>tf)Y6T}i80E@ z-+|DxEo9Oo;PNyoP_J0F#i^{WzaNv-vF5QH6SFrtsrHg!2b@n-DBr`;(UJ0(mX;QK z0H4DsL(@1v9$K6al8+TEq><%gU-5BAT&OSTy8x;?ceDU;@BZQT9AO=f3caUs9kU_m zw(MGa@qYgTkgL!s%)CQUzlZ;MV=tk4fRl!{&l`<&ya9X~2sxkWRRkzMK`c&5g0hjm z7$LvUU~Wg`-0KQ3IDKLX`<0#MJj9i{zmMj$q$X^pas1FhjGcGB5A0>oKp8%c_N8X; zn^{rI04uN!)b+mevJJ`T9BDC9PEJKq4MHSnerG$KVgn2LWUA8D70Cujceg=rg1GfNx6vUQfjjvJW(SpzZHX)h~Y>6!j%jLWCu_Ya|L3FYC z3gp&i(V^`O`urLz-CVb z=e)gdra_WM_2uBn+|0B$G6=$q*Cp43AMHd)>NU@AePyQ|M-7qViG%>x2q^OaSqp4* zry=Yv0pq~TgQIX+S8%PL{6+7qyR-9|CM9%P$it4s_oxOj!} z=(AsmVVM+(>N1lY%Dsdbz51K2z1{#vk{HTmfb&6Ka$InScHo&g+Y9HlbMd@nf_F1D z@c?AbSU9+mZ(HEJ2Il3iNF9jP{!-7&LHhv?J`Pp4{4H!B7-%F|gh&?u>Mb5ywuhZ! z|1Hm&AFon7I4}#LNZmbU|Mdy{P>k3MGC0!sT$36mF3hL$DvM&AV@1hbs9aPk=GU2K zkK8l_`4g0iQPep1>!I(e-cH&VS8{4UkydjDbKf|Ud}p&}0(fXwTsN3s}~4i5Q@thonNS3&FccR-X-3i!Z+CewpX0sqSzFg2K8B)pF%BcAerW! zSO$yHQ-hB@X5-qgrW3UBRG8COv{F?w_SEX#oX^?8apU)|NCloLu>bJV2n6Mrxhe=$ z&*RQ3lM((rGD5;9-^Q#r%EmD!Rk`U;{%^m&R1&0}l2!o1zAzy-26nPaKSY2>^CRMY zczz}UzGKu$M?X4(CKk%O3QK%kGNtC&$|URWL>Yive)^ytlLm{@)6}=|pJq}2M+obW zT@D&_)=3yAsO5}c>OkxP{d@p8D1s?U*^BrJ^xW^ZxC1I_`1Tg0P%v5=bE?5cLl}WY zWADRCVXo9)*!kr1b8&FT9WDhF(N$rlo-PJ@dbI#0^rc6JaEP#Q*a)UeK&xvLDwy5c z5bnvc(eCMq+^ors6w~nWK;pS|;D$q_v*?&A(NyEMa|OOH`~e70hOLsBt|qUzVBlbI z=o=&D*dGb~Q!ZUccc|PQ6AZK^_2hYy+(wlsnmv;$Mv9>y=uG*E)M1$1Jq)JHrgeX@ zeC@FO0oQN^c)A1I@Sq$MdkI(NX)Y-}Z+?Qt92hg673Jy)-Kc?8S>)NNtz9NB!lYfT z!H;I8seo-vXeoaYz02B(jcJ{?93-tu$|@@TL%@{yyMZ}@_V~QbkzqF-uyU3RL5!q| z)jX);olE3Trabjwj#8J^(l9*PVrfRA3h0O9O&3{fXxvGp6rvvQ2kZvI(@OP6r$gMTzVVJ&%IX!r(%d1bSL~6mnf%In^<*NaT22lqG#=yQm2zre zws$2&rqc2|*lp<4y%wYJGIhQVp)|A{n_meQZVTKZD{nszX`6YltkJzL?y*KbLu6Qe z(;rYOK4;5|gaD*lDR%p~3`wTLmMkdqp3-6fDNAftlvE}&BxKq;lI#}@E8K~C55RcI zChx=Gu3U{i+G~b?P+l{y&_raarjF zC=0l~TTbsVa_)k6av=iW#^6(T%oc7FU$Sj)3Y$oNT2TGx%KM!)(PjU?MrlUh3Sgr! z*5>5pB_QR0{YwO&a?2&H7mZsMhq`hb@2srGOW*g@o!G*!}OtW+a9A>u?J zX;#F$UW};NTD>iwf3~47zZjs{xQ4Co04MQc-LDRBT;25EoAlwBs90cI^kAr8x!zQF z7Qa6J6E6TG^$pL*f0}C(z0K=hf8OITpwu-&I_&h9 z>s4tkKzQEp%Fx6X7SKu2Gbvymw}b%56g~+N#LOTZxG3i7z;1MTUqbLRw~J>f9GW_xS2<33<@E zv?7e5_jH9_BWi=0JKB-dgRjTne z6q}apNz4Zhf}GAxa)&ERloPXlIvLn^LOLfbYh2x{s~i21d5g1OA+Z6>&o=Lp!gc`P zYtZUf>mBHz?LGh+)4$f|`L+*xWUt^xL|?KE?2~;YYVZolR~t{g`9-BIxzjU$89kX2 ze$QhgTD&dbJ3fvZuOGXDG}Kc_O)7FZX4Z@cYW8IRxsmGPVPbl-jNw}nDHSX5P@+Gy z><4(+&j1_)Z8p*ChyLApvdc+N*)Rumo#an0Uo3OFm!mxC{QVLKKvQ@&7V_-E9u(uB zW!yo<(=aW|9r*W)vn;JY5{vn8xT~=ScpK1tWobu{v5(`xoVN)uT&=zMe4ndJD+dB zgNf_8Dl`Wc(}*}ypdZQ2&~Kpc_)-xtbyNSI(c@+wMEzjl82?Emb{!*d;7jlw?9Bd3 zf1GlVwydNVUPwupT0Jnzp7TZ9&m^;NuIf(m+;+NbvJ)QOAZJyYrr^qu7i2Krbn@>X zb4g9Sy>Y5}CL>O8vPH`|^4m;jH9$n%sJ%RuK_mqmbkvG=ZooB4-TnMT7M19amS6XC zSse^*T+QVOQ?saz3}gF9F&1}hAL=QgD0_IyC2`hswN+y>jT_f!zCX1rQyUGVOb<#m z_7riD)8KD^28)*`F?z4aND|DJAsrNVihI!jZxJZ;r7P#>=QoxcG1?g^JC2;s;w{ia zw(dYdxWMti8H2p|;p#~lK^4O>StDC|`24zRji}c%KMQYAXS`ZjYqo5*njMub9NREX z=9?5F-0rzNRyO?<)g_0v1voIWNt^#GTwCnXcGUbbQZt;3ljQa@hxl(ZTH|q21j@Vx z=&SA<$^VEus|38-uga$Y}T-Nzbg^KQR?=f z-?dTmKKIOq=t*-fVt%f1;3SXxKO6T!Pi7SZ0I_TL_vi|OCc*<+0Oc-Ikx84Cxp9mQ zeG4&NC`$|xKiwu|+?2%~f27!f4B@x+bPDnDpq4F`1m~6g%=w0D3jLchv&0Z$l20|U zdn4MXtRjdHCbrio-kw+SJSn8$U@chP2DTFjqb6{oF{7B*P==7)ONY>5U|o zNcT)^$*XXYpub<{$be^It1-wHRKYm+g&nnxEjKK|#nP^L3nw-VpX8`GyM9JpuR?!! zI7wcYYv(Q&4LYub=SWzK8`q6MPEA&TCL9wWkBbh!rGy!WIgtiN7X`u zpCSeAzG?u2F!|tTC(5tUG>#4a$;p@e-0TE+ICW|@p^<55(m9Q# z?_`WpNfR7aS|}{fN{@9%51Me9jAsP$f@o;U_6p_12^ZIDQN{RLDc|rtSA^ZYVCTd9 z8+3JoMyl}ul4NGqO3PQD09^~iq~oZf0Ys#Hyu!gm!m!9cemzv-sA1}ReT4EzKL)K> zr5B$lP2xjLy$g(%m4p^OI=n>wcQ=U95+lZqmkpZlLS(_?DTk4Yf2vco{>VOIj&|?( z0iZm9o1J1{Q`OA3A5I2b@i{Ouf-D=0w?`kM9s8e_s2M4Dae>@Y=rXwm6^z|jnb^~OYyoM2DnC5mdLrgF{Vi z;HI=?BbH{y`LRSRiN>Rt+V7tU!DJcf@;HHcILq|-sA2KMRpO33pM>L;Lxx#!vpV0> zzMrm_YF~XtT>b&PU*h>bEN))+!|!zf10eiDkHT+k|8aNC@>>U?+qEh>v|qb8`IWG3 zo70W<;bhEakA-qsC#;rR-rQLIT~&|_z+)$c3SZymKFl)`7Pt7Ri@4(jPHx8eX|_8O zjP3>OHV=@nS>a4DuyA&MZ}w>Jf9JMXoiW4(=8?ZpxsHr#TAERRWu+>3n+CCf58zg-2NZ+Ksh0pGpvh+FN7?v z6(BPKiD8GJQ^`wa6Qrwnjpd>Tf!lMN@B#gq$~qcDRL~kU4&SqxX~X0 z;?n?v1>lu9DuFGWgzIPIbCeOO6q5$R5SGnpx52)EB1B2L$m%#Yn4X{u(Aco(Co7U*gM|EKccm>65{RXXi(xA zqf%|gik}%<6C5b;yqT3E&KfNizbym?bQGlrK*9wC41`{)^q}V2H)OQ&Xf!+yu1gts z{Ie2I)tyv(ElmWdXg{8be!Mo$6{L#egI>M4P>*vn*^O})-RKq+@ z9nmca*9aET2s}7Ev@L3C!ZRItZPsQ0 zLCX}5`;1gok%2>}H0ETI&-Lp86Ui}5H21o3%wSMnsQvqMcXPL6oNU9)Rss3)x!~@z z5s71Kd%df{!v_>Dininc-$o1XFa!GAGZ%hZ$CJGZgkRp5ZBvIiQ?9Y>dD@x6NTBjj zAh4L;7WprqSoq=QBX@iPfgv(2;wx;CKV^eTbc2}`PbSjBGUFY9MD{XU;{MT(df2i4 zCrbm1ZYg)_g-)OzK2F_5$@pqTM&~`ihefzCiKB92$$ce&_wO}74)2a}CI9KErl#i0 zzzGmZYWBtB^-37(f9Z2cV9=9%qznH>Z)_}{hXRb)7u?X+d=W1syV)C(1aMhM`oJ=5)#nec*&H%pmse`8hIS)J#zuV zHGqp?-(`QAeZDg|s`vO0&mxNo#Hio#TJ=1Mut(jw{nt)KNjV|m2>{@@t$@P6UC}?|M8_235Lbwj=x;kt{B>8-| z${y>zu&J{pVs z=qR)8C>Q{%R7g$s54Ngjkp1fkPT2~IW#d5?KB3}CK9yGg&qw;o>E@*l*Xi%&H4n@* z0RZluE9|>>$#J`Gl_D=!sIUKG5dupul#K+*H(<2wfQYB?vXB=NHoWVCi^6|l(7qiW zd+1I6p8U98=v9Mp=CGFt4?i?nviYxqEuUFnIG} zwOv75BVNq}r!SO@Q8E=~F<*wZPtfa~&gbV|Bs^p$0{-0>3_PG} zY|=`ymyvd3Ldr=^3D)+9qA9CJrKw9prM|g0@_95Z!5N|3QaV^gu~aiL9jJkyXihb3 zbCxU%FxGzZMYSNJn^Tj=VS7xN3NL@mw_1&kAtUQ%s7+p71j#LD*Z%+@T@w5m2>z_m zZl?HzGG@|gT!wf0(U52bx6^r|sE}X!t7(8$y2s;%m{g9SS*)wcAXD2&3Aw%J=mFhC zzS&-E0iscjeDuDn6f|#1pM}iT6P-_Z)KzQsXZ!HB&K=gUn5YfkN|y+sV&gFvHBLgx zE-)p}_x|Yvf7wX=I~d9kIF1fIxbYR6V>WJk*J0=PRTAYmXUhUj7pPGMjb__;d3k}| z)j5CCb;zDJvhxq|INEA?jQTK1;JxZgh^#juNRfjf3{p2$`Os$&)2^&N6n50jcJ3{( z`0TgDg1Cte$cJN|1wW{1!g_Tkot(h|xB(8gh;tv9>U$w*Vv8bT^wm#@~p zXUd}QLq(kR_3;4!F!R}MU3L}1d6&Ra28d{#NvFW*8^ClScD3fk29#vBffW#fUN}WT zdqNfLc&Uu>ze$KGUCtXP;qnMULg5FXuO7w-6%oL|V?JM?h4(|rCZPJ|Lhk#=9C%_< z4=-it5YJEU6|~B1CAf}S@E8W)u^R!yfi(wlU#D3DJrnoL@z5=9$%CylB(ASMfcZZ9 zL$sAa2DpT=e^6LS59}IS6fo7X9m#g7SD(paDgso;{#QIfvm(se8Ts^EhVC$Y2U%k- zFQcVCT=2t#?l?a?NNbY;N7UU{#gzKV;j5`xGi9aZ!N^TnT1UP0E2=Afo7yU@JUO}G zGiGRd!Ix&)IGh0IE$t;>Xb-ijGayNSj7E;^#Eb0^eJSquP}2?rwI7PHNMKcALuCO3 zemQ@)ioMvog~{F6TaKFtY^se**K^9b_KvYiJjcDa6I2aMP05IcX1AO)5c@Es>RT{i@h2ny1bjH5^hStTG+`tFSouujj*u z{Perj+@P$GS#kwV6tBsP8LU&X19>X_0^QMeqwPiV2_`X3^g2fm#(cp3wn9Imr70(6mygz!W z@sc4NwFz&E{{2G;od~c!^<4DinE^4h7j>wGZ~m?7mmg^>MhM^j5Zetc(etz)bqpUW zPO+id_Q5(iQV$~U3$s}_;<6_tJjGH(;j|Bp%;s+=+Bu|$u>IIur1&894qt63NugJ9 z`4{P%N8ug|qkq8UriWbQ{Pf)3%w4>sDRybK?{vR}c@SA^J%^`KcN^!^8Go;0TDz|@ zZx-*i2+(a1xY7pb@FLs#R!5Rpp6LD_jQ9I*RAA8pcv&^SDG}6*Nn>96hEHE6kZeyz z@!NLMty{;X|9~ehdS;=CQh4{p0b5?#qoq_6gmA`7gA&Y*P^&cLqy{L0xh$5{i*MZqa{GH{us*}k{P^_o-~D&D?qp`6X{e1T7K zelfGEy}3k1MHMC#gEwBenX4bWuwi%}xc)L&s$x|O?p27P1-Zp+7VvET^kIB$_R{#< zCzPl)EFwlGtNKzL>HA)d@SAFDsxUI9#8+0PjPOnc+J2|6&y?o5sI7I^wPhut+4SML z<8xz}NwrrR+~d`{iy=3H7rEcRH}TGu^|pi|NIuS`@|nfqyoo=5GI7OV7U1(y`e{fm zWuNNm9v2l44zN`;OeU2MQe!%&tyZRDB=*rFwzp98p`*+G$}kT~vmCDK@Bc%DjXq_q zq^?sY$v)qf(XXX!C;vo2Q}N&<9M1yn=Zl?$vLNpEb3yhG=;`TSDsrl;eb3caQYs^t zI>!tt+R2s@SvUt+V@WD5fj=ghzK$-|A>2p(4|%JmIrSgM4(h15#%lu!xK4?+Av-8lW6U3d=W(ay zV1`2}m&@!pib$M?JxdlNgVRlWZuO@(DOuk*U^)8>|8y#L6*m8s_SDiV?dWTkA`1RU zIEjmuFC|3~dapP|XDwT7k2#y#C471O`EuiOOjAac$|jzvlO6!>@%vCS=fxtZ(y^5V z$*cdZkYOLNp18@15p#{XTewLNKibvrm139|CO=Q*W}B&@miy}3XRF>KOjNxqg{e)j z*C?X*t__?(IVRB0GMNJ2AJ2)*&(hw(Hc+hAd-ta_5W^&(ddBYv7s(@IkOS|OI3(C4 zf>~IpJ;yEyv0l)1%2IVp?t&6SvY|6OtQjqXqH{0Y*{Muh{d#u)V{L#93m;8l$S9_* zG)v^vuih61DZFY`F+O)TCrR7zr)jAi{4T`1>7b$K8e43LJ#sneudE5YR)Po2hg%~U z`DbU3GnKR8rm<50?!FJ5zx<|hj}lv69brs@031N6s*l2?CsHQ5VJQaxYP+*nX*OMU zsd*iQ7$m5~^_MRX6AIi|(BiV+aKWo%8=&QyX(Sk@P!K2nE*4XXO)8?WGF#wnn#ei8 z6#}X(+scqvd_rY`D1zQ5mA#}Fl|9*L!5V+8ub#kozwWdFE}yQ_Wh3*M3nVYM z=i@CwxhotNrT^9&c*4&TP$%X&U+!U&tjAMj8jPHenpX$q5Xo04w|}BsOA3jdCIAgn z3I3M%0aeY+n}B{+roaE(UUho~`_sB!oI8i< zk)pdLQtIDzB1W6$Ww|xTlUJCzYybOI(*e1V+4uIe>-|O*4Co>uUG3C)9z>n7bdeXM zAk75l>5EWROk&8eN=dskTW-3w-+o$nY{a*!F@O0fXL+9NkK%p`T)JC3tV=4iBp*7% z-%}!#8DT(+`FnL1Eu*cE3ZHOqYk_&;lO;Gu*z?@I8)lJq@i$X+|77WwHV}Ey8yG9+ zXyh{hYeLJy5mjO`8V?5`f#kcx8*MJN*|hrOK)?S_V5h$a|Jd6%%`(A~nw0^zYLX^Z z6o#H<;>DiLCF8fhVvMJu(W&T{U&xM!ysr44nh9tU3q0_UMyB$=(Mw~LA0tVciDV(> z83?+C)ou|sSn#T&RH$|l^f11ORCcFDg2Ly~d&f$q)6LR1Ns`UEBi?g?Vu5n|dZpH< z+L;xykZGT#G9jH$c|C_=m3BuaGvf&HvH9@#%1~GmcTY(x%%e|aaE=ClgihLpWDy~JpjtQ8F}>z{NinCE6f&NrFOm>7w=gQs36&MkZK>RtNs}K9F&V?D8yq&N>go=Y8akDb z)nLB#-<5%a2XbyExRF!v7(QAUyB4d<8dA$Ks8>`Ip*_vsQ<+B_FdUDzOm5t+L5y@_hSACy)4T*Zs z)*4*%tsuyu(o5dj<9M%A3_M@OB|9)Ytm31XwpuZb*#udTq2BuLee z0Kt?LdJJlepRr4dypH1y+rQp3TF0(o$S@UZdsRpNI$Ao%52w#<0sAhTz-RHXhx2hH?x}}*&`*zDy zw{oi!rU=<6U~q3k_2f6|((%{95}a{;KeR<(Yw9M^Wmm1*+jX5#k9JJzaH=R=8K-W3 z7ojo`L?kzkz9Ey3A5Vu`Rr=E9b!t&alSVj7t`?9Yh~!Vll%@nqTP?bwKS(v^};e99oX8 zyu8g*6>X$)%z^w}m7_7voT-+|%NX~rcp}{sV$=^GTRlZDBvpS=L4r)vf|IOC(hJU74jh$u-JLG%>xT#Z#`*3{=EQyC&` zEc*xe75&R~qC7}Y5|eccxgs-q%F_OH^{yEpNmF~&l5%KdyzK_X7Jp_sinqB+ZTZSw(-xCeg01KpmWcBNgto9r z`b5!<^w12=ZM8g&u@>&>q}A)ruGq6}(Hu{n{u-I`T09ToTk3?JgR3X( zG`AS!i6QLNoh1YE&y^t>h{pH%`m4rKsyieYo4|R^po=n!Ak?)P&;iHlyW_T;ttQ_S zD@d6wmaTV*6_LJ&RlqkO0mw@X7B!_JMElxSrTZO zmO(5hq|5=!0Le4zT9<_gElBvrA+tQM3Z41O_ zMVe)BbI&`W{VfJP6D{g-cjnG$2N9o|Qo>diu8ERrY47t+B=QCpE`nJ`$FG3bKYeyt z;PB*IlJDV6h}xdNmUV3|b<-xo+kW9|r3_sbSl{*w$$ zOZ^uCB!m7jAf9{sup48=BHZ>|2+f&i>>x@r}K9PcQxszqlEQjhQ(o#|wBhVW5jL zX7!tz<|RL-i%f}klx9+uuxy_RqF}cw-(W>I4#9GID*SdQs zE%H=$Br*p{+awGpIfnf}14@(y%=rcSlD!J{y`%_42oY@x-LtL)zV7ZHF{uatTf9C# z)vVcPp46yH`&}Jbu0KMS@>y0+SqDCgmH_fM03!|01`DYUN)U$T-ZyAySJj$1@(fUM zkusrc)!Ty_54X5FN9Gi{^Vl(}0g>s_)->!#gek1q6%({f$10%Tog#wTtLQ3W?A4ry z1fLb^@2D)nBjI{i;C%||u1za7i~6I*g1V~YVrNSr33}<|t0F$BBJ>o^cTJxOAbX_$ zshiJ>7wtge+#ny87?XvnA!O~{08>A?Z3SN0mwli3?W314av)>#jl!?DqKpa80*(H| zgNj3d$(JjBit$Sq~Yk($=q$0BE4bgZwXp;p}M zdg2cB{CQgjt|ro+aimM=Tn8%I^sqDi8KqyZ=b8}4b3A4Qd>r^_1tfIA4;wjD89V1| ztql!cYmZB7Cti<-rzUjN|MgFRTC8+nV}GdZ8k)aZ$4v}xQj!D!ug3};#61LA$s1u* zT_!TP1@at7Fw!}Hh2Pw;kaEJ(9ZLF{2fbYUGm83@N$CZ-re=NFo>-eLrj|4Xb}BTj zwE<^N!k6nL?waHS&2K*OpTzd(MiZO_9%SV}Y!(=pJQjQC?1x3AEne)ckh(Mbqxd?m z!&l^?{A&4NXt0fAq@dOJb^>S|;6#T!2^R`k#Zp3CITxin{m}B6p70>HRgr4kW88zc zUZNJcOxlbT2QNNw_<9@r(9U^Kb5Dba0UMcyMM||-F#gV`%!1M(jA-ny?tRQ^ZnU_Z zdDBccy(`ROA>RN~qc+R{3v{r?rICZmEtglacI+!c75&+PdXI#S*lyQSnyLcii^EhQ zjZQjYZn90gU^`=8ewWDlb_Cn6hBv`v;HtKe3L8E_oH7!x6|IXu~-{$}lqg+0LT(tPgRR;>f|#|DJj)?iRQE z&z_5Pk;UYifdPS+57-q$`vZ`FqnP=w!~Z=jrw?z3eET+d-;4cwjPqM;o-{$*$W>u_RsrK^CYrs@Oa#R|G9Sid-be~r^Ijf--d+W8(+;|EV~9YGq z8xoaAO%$N@mX{i%FNk{rgnEE=gpe|=<1_w2<#w|EnV4{CNAqU3e8;5&xTRs{Hl zoDgn+NVNr|h=HFCP|;=~dJ%BP5wA*Or=ti8KT?W8%%)(mt}Zs*PKWaKhw<0gEozSe zgj?Otwyl<2{6vOR>*NVZxq>xkf87w-u?fH5EQXGN=1gMRfG`m@JFdd3+}4dzVsNSa zwnBt#D#kaUjoIJODe7+RG-JD^8W{a+cP985HSY|Vjl9}NLgKEM0Ja*E+D%+G-z)A{ zp9tenk-l=-j=LUPXTWOnWCQuD%;881+sKUE(Jt27%)b5JKZSn5yQ%!%$g>*_=)k;Y5`ww7 zts?-K_T}^<-aCrWVtt!pp&l*pJ#o1mzT1D<);1)e(Z!o_cenuvjyiI#jbl z92iIOFWk{Jz#KP1wP4n4VJ7wKWDDx2?^sPZ-6myyKop2{GTdiwM2 z;?w4{Y7PxZKqe;U7#O*b!E0gv0*16MpmPjZW87>X7KiM~Opv`3rbv*o(VEWuRID1m z2gZO$k54gqh*-fD(dJ+EPF3+PnbkKydd@60LBB8Q~bX(p=F%NopY9sr%liu4(ERLTL zMqE9!F%%zzpvXKKq?fO<^nrg!?BPYG^TXg~iI)Z*WWRyX8<4Dz%4j9F!>n2*JeZSc z^2riHy3DOG02|6zMyFYjhxuVbHLxCg;=haOs$lzU{R7q^@vi^obkA-(#GFKOn%n|8 zcjW_ISNmG_WCR?5!OwT4(vIkD=*89%dFF2j0$5<#s4hwSo_MjYsC3_i-Za}@OV)#KWrD;Tu=#@92S-KPI`C_ynrOh?eFA}y(LYIwtAnz-G9aJo2?l*+ z^0NC=9z?5bO|*TRts3SRYqEAc&N2S^2?f99)G(}%$t<57Jm8}2OyN`u1!`D`(@2GH zEB1i_PO@B~Md|_%z>6;7BSePn&Ucz;3eS=yg4$Ibc>^z zoRB_o4FtLqrq%z6zvW0zNwbrmxv>V&~VEovntN zq+#Dc8{m=Hx@;FaEqAs=0bi(lUh9@;O>1#!)Bkk;>(k7!^|KOW%7)>j`?}7n3P}%v zvX>+j12{er{fgeCFBt`b*p?tm>?;*L#%mhG-9|{+^^i z*kZ!NfoFjweR|iAm`4CYuH2oX=uKn6!#$`Tlo2m+vdXn?zyT5*Wc4wWtNhZ&lwRTi zty0lQ*g0ypk7FMD+a(;N+D%`ALqRBVb=R|wFuwBfuKofSq|61>6|gCJlo5meX>mU)oM1L=X=D0pK~g%Rj*Ee8CdEMjQ*? zEb*y46&I=j%MTK}-Fugso}?zYZak*Fh&jFFg>q8;VCG)>@d};1@yMJ#Msun!mnt*H zT}_U`Jcu?`IY)=rYdQq?hB)c#$^4fcIX>A8JO`n9Unb!k-KfpL>t?_6vRD~ty zI`z^`%QC1ps2GHdxeR1u`OES?&b#->z-&qrb9|)p*W2^vQ3x&8)0}QDQ_2s6c`q%d z2?{OHd-8q3L_1nAcsKKVaD`@nLdoLUvJtrn$ni7}{15!@``@5(`}uS!^hUcA>8twJ zKyCH!rfv43R%KZkPmh>PYpHyl)5*a$t~upzVimJ|Y95SKIW`K_{pZf%{~3LLL%xlU&$iTp;`M%rCDs{K4s{ zreRM&rh^Sz1dfj{(X%-qoY6kadboymM_%SabQ#?(#)R)tHBrbU^nq}{>?}Srq;jcz z{Au-Z8@0JYkvcPblir5GY|9c) zT0hc)Q89O(Jz`07Vj(OrOx(a|8f~!;rq3jcFK|ns9|1Pbv%o0x`>vSasOW}5*ZNkx wPx6^^W}6!C?24m_)Od^3pro&j&sU!PHBsYjs7~_SLBQ81DMiWhkA{K&2MO=rQ2+n{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/postFX/art/noise.png b/Templates/BaseGame/game/data/postFX/art/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..ebff74256171269adf17aebbf663dd1cfc4f08d7 GIT binary patch literal 14610 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 IM6N<$f@#CI-2eap literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/postFX/art/null_color_ramp.png b/Templates/BaseGame/game/data/postFX/art/null_color_ramp.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5a52186eb93cb7ace739e645e5e8f8e6b8050d GIT binary patch literal 2843 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 + \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/GammaPostFX.cs b/Templates/BaseGame/game/data/postFX/scripts/client/GammaPostFX.cs new file mode 100644 index 000000000..0b7d98403 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/GammaPostFX.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton ShaderData( GammaShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gammaP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/gammaP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$colorCorrectionTex"; + + pixVersion = 2.0; +}; + +singleton GFXStateBlockData( GammaStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; +}; + +singleton PostEffect( GammaPostFX ) +{ + isEnabled = true; + allowReflectPass = true; + + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 9999; + + shader = GammaShader; + stateBlock = GammaStateBlock; + + texture[0] = "$backBuffer"; + texture[1] = $HDRPostFX::colorCorrectionRamp; + + targetFormat = getBestHDRFormat(); +}; + +function GammaPostFX::preProcess( %this ) +{ + if ( %this.texture[1] !$= $HDRPostFX::colorCorrectionRamp ) + %this.setTexture( 1, $HDRPostFX::colorCorrectionRamp ); +} + +function GammaPostFX::setShaderConsts( %this ) +{ + %clampedGamma = mClamp( $pref::Video::Gamma, 2.0, 2.5); + %this.setShaderConst( "$OneOverGamma", 1 / %clampedGamma ); + %this.setShaderConst( "$Brightness", $pref::Video::Brightness ); + %this.setShaderConst( "$Contrast", $pref::Video::Contrast ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/MLAA.cs b/Templates/BaseGame/game/data/postFX/scripts/client/MLAA.cs new file mode 100644 index 000000000..db5fa8f8a --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/MLAA.cs @@ -0,0 +1,186 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +// NOTE: This is currently disabled in favor of FXAA. See +// core\scripts\client\canvas.cs if you want to re-enable it. + +singleton GFXStateBlockData( MLAA_EdgeDetectStateBlock : PFX_DefaultStateBlock ) +{ + // Mark the edge pixels in stencil. + stencilDefined = true; + stencilEnable = true; + stencilPassOp = GFXStencilOpReplace; + stencilFunc = GFXCmpAlways; + stencilRef = 1; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +singleton ShaderData( MLAA_EdgeDetectionShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/offsetV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/edgeDetectionP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/gl/edgeDetectionP.glsl"; + + samplerNames[0] = "$colorMapG"; + samplerNames[1] = "$prepassMap"; + + pixVersion = 3.0; +}; + +singleton GFXStateBlockData( MLAA_BlendWeightCalculationStateBlock : PFX_DefaultStateBlock ) +{ + // Here we want to process only marked pixels. + stencilDefined = true; + stencilEnable = true; + stencilPassOp = GFXStencilOpKeep; + stencilFunc = GFXCmpEqual; + stencilRef = 1; + + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampLinear; + samplerStates[2] = SamplerClampPoint; +}; + +singleton ShaderData( MLAA_BlendWeightCalculationShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/passthruV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/blendWeightCalculationP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/gl/passthruV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/mlaa/gl/blendWeightCalculationP.glsl"; + + samplerNames[0] = "$edgesMap"; + samplerNames[1] = "$edgesMapL"; + samplerNames[2] = "$areaMap"; + + pixVersion = 3.0; +}; + +singleton GFXStateBlockData( MLAA_NeighborhoodBlendingStateBlock : PFX_DefaultStateBlock ) +{ + // Here we want to process only marked pixels too. + stencilDefined = true; + stencilEnable = true; + stencilPassOp = GFXStencilOpKeep; + stencilFunc = GFXCmpEqual; + stencilRef = 1; + + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampLinear; + samplerStates[2] = SamplerClampPoint; +}; + +singleton ShaderData( MLAA_NeighborhoodBlendingShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFx/mlaa/offsetV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/postFx/mlaa/neighborhoodBlendingP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/postFx/mlaa/gl/neighborhoodBlendingP.glsl"; + + samplerNames[0] = "$blendMap"; + samplerNames[1] = "$colorMapL"; + samplerNames[2] = "$colorMap"; + + pixVersion = 3.0; +}; + + +singleton PostEffect( MLAAFx ) +{ + isEnabled = false; + + allowReflectPass = false; + renderTime = "PFXAfterDiffuse"; + + texture[0] = "$backBuffer"; //colorMapG + texture[1] = "#prepass"; // Used for depth detection + + target = "$outTex"; + targetClear = PFXTargetClear_OnDraw; + targetClearColor = "0 0 0 0"; + + stateBlock = MLAA_EdgeDetectStateBlock; + shader = MLAA_EdgeDetectionShader; + + // The luma calculation weights which can be user adjustable + // per-scene if nessasary. The default value of... + // + // 0.2126 0.7152 0.0722 + // + // ... is the HDTV ITU-R Recommendation BT. 709. + lumaCoefficients = "0.2126 0.7152 0.0722"; + + // The tweakable color threshold used to select + // the range of edge pixels to blend. + threshold = 0.1; + + // The depth delta threshold used to select + // the range of edge pixels to blend. + depthThreshold = 0.01; + + new PostEffect() + { + internalName = "blendingWeightsCalculation"; + + target = "$outTex"; + targetClear = PFXTargetClear_OnDraw; + + shader = MLAA_BlendWeightCalculationShader; + stateBlock = MLAA_BlendWeightCalculationStateBlock; + + texture[0] = "$inTex"; // Edges mask + texture[1] = "$inTex"; // Edges mask + texture[2] = "data/postFX/art/AreaMap33.dds"; + }; + + new PostEffect() + { + internalName = "neighborhoodBlending"; + + shader = MLAA_NeighborhoodBlendingShader; + stateBlock = MLAA_NeighborhoodBlendingStateBlock; + + texture[0] = "$inTex"; // Blend weights + texture[1] = "$backBuffer"; + texture[2] = "$backBuffer"; + }; +}; + +function MLAAFx::setShaderConsts(%this) +{ + %this.setShaderConst("$lumaCoefficients", %this.lumaCoefficients); + %this.setShaderConst("$threshold", %this.threshold); + %this.setShaderConst("$depthThreshold", %this.depthThreshold); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/MotionBlurFx.cs b/Templates/BaseGame/game/data/postFX/scripts/client/MotionBlurFx.cs new file mode 100644 index 000000000..d6d86f63a --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/MotionBlurFx.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton ShaderData( PFX_MotionBlurShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; //we use the bare-bones postFxV.hlsl + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/motionBlurP.hlsl"; //new pixel shader + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/motionBlurP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; + + pixVersion = 3.0; +}; + +singleton PostEffect(MotionBlurFX) +{ + isEnabled = false; + + renderTime = "PFXAfterDiffuse"; + + shader = PFX_MotionBlurShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$backbuffer"; + texture[1] = "#prepass"; + target = "$backBuffer"; +}; + +function MotionBlurFX::setShaderConsts(%this) +{ + %this.setShaderConst( "$velocityMultiplier", 3000 ); +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/caustics.cs b/Templates/BaseGame/game/data/postFX/scripts/client/caustics.cs new file mode 100644 index 000000000..85267be78 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/caustics.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( PFX_CausticsStateBlock : PFX_DefaultStateBlock ) +{ + blendDefined = true; + blendEnable = true; + blendSrc = GFXBlendOne; + blendDest = GFXBlendOne; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerWrapLinear; + samplerStates[2] = SamplerWrapLinear; +}; + +singleton ShaderData( PFX_CausticsShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/caustics/causticsP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/caustics/gl/causticsP.glsl"; + + samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$causticsTex0"; + samplerNames[2] = "$causticsTex1"; + + pixVersion = 3.0; +}; + +singleton PostEffect( CausticsPFX ) +{ + isEnabled = false; + renderTime = "PFXAfterDiffuse"; + renderBin = "ObjTranslucentBin"; + //renderPriority = 0.1; + + shader = PFX_CausticsShader; + stateBlock = PFX_CausticsStateBlock; + texture[0] = "#prepass"; + texture[1] = "data/postFX/art/caustics_1"; + texture[2] = "data/postFX/art/caustics_2"; + target = "$backBuffer"; +}; diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/chromaticLens.cs b/Templates/BaseGame/game/data/postFX/scripts/client/chromaticLens.cs new file mode 100644 index 000000000..fbf52d915 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/chromaticLens.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +$CAPostFx::enabled = false; + +/// The lens distortion coefficient. +$CAPostFx::distCoeffecient = -0.05; + +/// The cubic distortion value. +$CAPostFx::cubeDistortionFactor = -0.1; + +/// The amount and direction of the maxium shift for +/// the red, green, and blue channels. +$CAPostFx::colorDistortionFactor = "0.005 -0.005 0.01"; + + +singleton GFXStateBlockData( PFX_DefaultChromaticLensStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; +}; + +singleton ShaderData( PFX_ChromaticLensShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/chromaticLens.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/chromaticLens.glsl"; + + samplerNames[0] = "$backBuffer"; + + pixVersion = 3.0; +}; + +singleton PostEffect( ChromaticLensPostFX ) +{ + renderTime = "PFXAfterDiffuse"; + renderPriority = 0.2; + isEnabled = false; + allowReflectPass = false; + + shader = PFX_ChromaticLensShader; + stateBlock = PFX_DefaultChromaticLensStateBlock; + texture[0] = "$backBuffer"; + target = "$backBuffer"; +}; + +function ChromaticLensPostFX::setShaderConsts( %this ) +{ + %this.setShaderConst( "$distCoeff", $CAPostFx::distCoeffecient ); + %this.setShaderConst( "$cubeDistort", $CAPostFx::cubeDistortionFactor ); + %this.setShaderConst( "$colorDistort", $CAPostFx::colorDistortionFactor ); +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/default.postfxpreset.cs b/Templates/BaseGame/game/data/postFX/scripts/client/default.postfxpreset.cs new file mode 100644 index 000000000..10f986516 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/default.postfxpreset.cs @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +$PostFXManager::Settings::EnableVignette = "1"; +$PostFXManager::Settings::EnableDOF = "1"; +$PostFXManager::Settings::EnabledSSAO = "1"; +$PostFXManager::Settings::EnableHDR = "1"; +$PostFXManager::Settings::EnableLightRays = "1"; +$PostFXManager::Settings::EnablePostFX = "1"; +$PostFXManager::Settings::Vignette::VMax = "0.6"; +$PostFXManager::Settings::DOF::BlurCurveFar = ""; +$PostFXManager::Settings::DOF::BlurCurveNear = ""; +$PostFXManager::Settings::DOF::BlurMax = ""; +$PostFXManager::Settings::DOF::BlurMin = ""; +$PostFXManager::Settings::DOF::EnableAutoFocus = ""; +$PostFXManager::Settings::DOF::EnableDOF = ""; +$PostFXManager::Settings::DOF::FocusRangeMax = ""; +$PostFXManager::Settings::DOF::FocusRangeMin = ""; +$PostFXManager::Settings::HDR::adaptRate = "2"; +$PostFXManager::Settings::HDR::blueShiftColor = "1.05 0.97 1.27"; +$PostFXManager::Settings::HDR::brightPassThreshold = "1"; +$PostFXManager::Settings::HDR::enableBloom = "1"; +$PostFXManager::Settings::HDR::enableBlueShift = "0"; +$PostFXManager::Settings::HDR::enableToneMapping = "0.5"; +$PostFXManager::Settings::HDR::gaussMean = "0"; +$PostFXManager::Settings::HDR::gaussMultiplier = "0.3"; +$PostFXManager::Settings::HDR::gaussStdDev = "0.8"; +$PostFXManager::Settings::HDR::keyValue = "0.18"; +$PostFXManager::Settings::HDR::minLuminace = "0.001"; +$PostFXManager::Settings::HDR::whiteCutoff = "1"; +$PostFXManager::Settings::LightRays::brightScalar = "0.75"; +$PostFXManager::Settings::LightRays::decay = "1.0"; +$PostFXManager::Settings::LightRays::density = "0.94"; +$PostFXManager::Settings::LightRays::numSamples = "40"; +$PostFXManager::Settings::LightRays::weight = "5.65"; +$PostFXManager::Settings::SSAO::blurDepthTol = "0.001"; +$PostFXManager::Settings::SSAO::blurNormalTol = "0.95"; +$PostFXManager::Settings::SSAO::lDepthMax = "2"; +$PostFXManager::Settings::SSAO::lDepthMin = "0.2"; +$PostFXManager::Settings::SSAO::lDepthPow = "0.2"; +$PostFXManager::Settings::SSAO::lNormalPow = "2"; +$PostFXManager::Settings::SSAO::lNormalTol = "-0.5"; +$PostFXManager::Settings::SSAO::lRadius = "1"; +$PostFXManager::Settings::SSAO::lStrength = "10"; +$PostFXManager::Settings::SSAO::overallStrength = "2"; +$PostFXManager::Settings::SSAO::quality = "0"; +$PostFXManager::Settings::SSAO::sDepthMax = "1"; +$PostFXManager::Settings::SSAO::sDepthMin = "0.1"; +$PostFXManager::Settings::SSAO::sDepthPow = "1"; +$PostFXManager::Settings::SSAO::sNormalPow = "1"; +$PostFXManager::Settings::SSAO::sNormalTol = "0"; +$PostFXManager::Settings::SSAO::sRadius = "0.1"; +$PostFXManager::Settings::SSAO::sStrength = "6"; +$PostFXManager::Settings::ColorCorrectionRamp = "data/postFX/art/null_color_ramp.png"; diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/dof.cs b/Templates/BaseGame/game/data/postFX/scripts/client/dof.cs new file mode 100644 index 000000000..fd47c4ae2 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/dof.cs @@ -0,0 +1,599 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/* + +================================================================================ + The DOFPostEffect API +================================================================================ + +DOFPostEffect::setFocalDist( %dist ) + +@summary +This method is for manually controlling the focus distance. It will have no +effect if auto focus is currently enabled. Makes use of the parameters set by +setFocusParams. + +@param dist +float distance in meters + +-------------------------------------------------------------------------------- + +DOFPostEffect::setAutoFocus( %enabled ) + +@summary +This method sets auto focus enabled or disabled. Makes use of the parameters set +by setFocusParams. When auto focus is enabled it determines the focal depth +by performing a raycast at the screen-center. + +@param enabled +bool + +-------------------------------------------------------------------------------- + +DOFPostEffect::setFocusParams( %nearBlurMax, %farBlurMax, %minRange, %maxRange, %nearSlope, %farSlope ) + +Set the parameters that control how the near and far equations are calculated +from the focal distance. If you are not using auto focus you will need to call +setFocusParams PRIOR to calling setFocalDist. + +@param nearBlurMax +float between 0.0 and 1.0 +The max allowed value of near blur. + +@param farBlurMax +float between 0.0 and 1.0 +The max allowed value of far blur. + +@param minRange/maxRange +float in meters +The distance range around the focal distance that remains in focus is a lerp +between the min/maxRange using the normalized focal distance as the parameter. +The point is to allow the focal range to expand as you focus farther away since this is +visually appealing. + +Note: since min/maxRange are lerped by the "normalized" focal distance it is +dependant on the visible distance set in your level. + +@param nearSlope +float less than zero +The slope of the near equation. A small number causes bluriness to increase gradually +at distances closer than the focal distance. A large number causes bluriness to +increase quickly. + +@param farSlope +float greater than zero +The slope of the far equation. A small number causes bluriness to increase gradually +at distances farther than the focal distance. A large number causes bluriness to +increase quickly. + +Note: To rephrase, the min/maxRange parameters control how much area around the +focal distance is completely in focus where the near/farSlope parameters control +how quickly or slowly bluriness increases at distances outside of that range. + +================================================================================ + Examples +================================================================================ + +Example1: Turn on DOF while zoomed in with a weapon. + +NOTE: These are not real callbacks! Hook these up to your code where appropriate! + +function onSniperZoom() +{ + // Parameterize how you want DOF to look. + DOFPostEffect.setFocusParams( 0.3, 0.3, 50, 500, -5, 5 ); + + // Turn on auto focus + DOFPostEffect.setAutoFocus( true ); + + // Turn on the PostEffect + DOFPostEffect.enable(); +} + +function onSniperUnzoom() +{ + // Turn off the PostEffect + DOFPostEffect.disable(); +} + +Example2: Manually control DOF with the mouse wheel. + +// Somewhere on startup... + +// Parameterize how you want DOF to look. +DOFPostEffect.setFocusParams( 0.3, 0.3, 50, 500, -5, 5 ); + +// Turn off auto focus +DOFPostEffect.setAutoFocus( false ); + +// Turn on the PostEffect +DOFPostEffect.enable(); + + +NOTE: These are not real callbacks! Hook these up to your code where appropriate! + +function onMouseWheelUp() +{ + // Since setFocalDist is really just a wrapper to assign to the focalDist + // dynamic field we can shortcut and increment it directly. + DOFPostEffect.focalDist += 8; +} + +function onMouseWheelDown() +{ + DOFPostEffect.focalDist -= 8; +} +*/ + +/// This method is for manually controlling the focal distance. It will have no +/// effect if auto focus is currently enabled. Makes use of the parameters set by +/// setFocusParams. +function DOFPostEffect::setFocalDist( %this, %dist ) +{ + %this.focalDist = %dist; +} + +/// This method sets auto focus enabled or disabled. Makes use of the parameters set +/// by setFocusParams. When auto focus is enabled it determine the focal depth +/// by performing a raycast at the screen-center. +function DOFPostEffect::setAutoFocus( %this, %enabled ) +{ + %this.autoFocusEnabled = %enabled; +} + +/// Set the parameters that control how the near and far equations are calculated +/// from the focal distance. If you are not using auto focus you will need to call +/// setFocusParams PRIOR to calling setFocalDist. +function DOFPostEffect::setFocusParams( %this, %nearBlurMax, %farBlurMax, %minRange, %maxRange, %nearSlope, %farSlope ) +{ + %this.nearBlurMax = %nearBlurMax; + %this.farBlurMax = %farBlurMax; + %this.minRange = %minRange; + %this.maxRange = %maxRange; + %this.nearSlope = %nearSlope; + %this.farSlope = %farSlope; +} + +/* + +More information... + +This DOF technique is based on this paper: +http://http.developer.nvidia.com/GPUGems3/gpugems3_ch28.html + +================================================================================ +1. Overview of how we represent "Depth of Field" +================================================================================ + +DOF is expressed as an amount of bluriness per pixel according to its depth. +We represented this by a piecewise linear curve depicted below. + +Note: we also refer to "bluriness" as CoC ( circle of confusion ) which is the term +used in the basis paper and in photography. + + +X-axis (depth) +x = 0.0----------------------------------------------x = 1.0 + +Y-axis (bluriness) +y = 1.0 + | + | ____(x1,y1) (x4,y4)____ + | (ns,nb)\ <--Line1 line2---> /(fe,fb) + | \ / + | \(x2,y2) (x3,y3)/ + | (ne,0)------(fs,0) +y = 0.0 + + +I have labeled the "corners" of this graph with (Xn,Yn) to illustrate that +this is in fact a collection of line segments where the x/y of each point +corresponds to the key below. + +key: +ns - (n)ear blur (s)tart distance +nb - (n)ear (b)lur amount (max value) +ne - (n)ear blur (e)nd distance +fs - (f)ar blur (s)tart distance +fe - (f)ar blur (e)nd distance +fb - (f)ar (b)lur amount (max value) + +Of greatest importance in this graph is Line1 and Line2. Where... +L1 { (x1,y1), (x2,y2) } +L2 { (x3,y3), (x4,y4) } + +Line one represents the amount of "near" blur given a pixels depth and line two +represents the amount of "far" blur at that depth. + +Both these equations are evaluated for each pixel and then the larger of the two +is kept. Also the output blur (for each equation) is clamped between 0 and its +maximum allowable value. + +Therefore, to specify a DOF "qualify" you need to specify the near-blur-line, +far-blur-line, and maximum near and far blur value. + +================================================================================ +2. Abstracting a "focal depth" +================================================================================ + +Although the shader(s) work in terms of a near and far equation it is more +useful to express DOF as an adjustable focal depth and derive the other parameters +"under the hood". + +Given a maximum near/far blur amount and a near/far slope we can calculate the +near/far equations for any focal depth. We extend this to also support a range +of depth around the focal depth that is also in focus and for that range to +shrink or grow as the focal depth moves closer or farther. + +Keep in mind this is only one implementation and depending on the effect you +desire you may which to express the relationship between focal depth and +the shader paramaters different. + +*/ + +//----------------------------------------------------------------------------- +// GFXStateBlockData / ShaderData +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( PFX_DefaultDOFStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampPoint; +}; + +singleton GFXStateBlockData( PFX_DOFCalcCoCStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; +}; + +singleton GFXStateBlockData( PFX_DOFDownSampleStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampPoint; +}; + +singleton GFXStateBlockData( PFX_DOFBlurStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +singleton GFXStateBlockData( PFX_DOFFinalStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; + samplerStates[2] = SamplerClampLinear; + samplerStates[3] = SamplerClampPoint; + + blendDefined = true; + blendEnable = true; + blendDest = GFXBlendInvSrcAlpha; + blendSrc = GFXBlendOne; +}; + +singleton ShaderData( PFX_DOFDownSampleShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_DownSample_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_DownSample_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_DownSample_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_DownSample_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$depthSampler"; + + pixVersion = 3.0; +}; + +singleton ShaderData( PFX_DOFBlurYShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_Gausian_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_Gausian_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_Gausian_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_Gausian_P.glsl"; + + samplerNames[0] = "$diffuseMap"; + + pixVersion = 2.0; + defines = "BLUR_DIR=float2(0.0,1.0)"; +}; + +singleton ShaderData( PFX_DOFBlurXShader : PFX_DOFBlurYShader ) +{ + defines = "BLUR_DIR=float2(1.0,0.0)"; +}; + +singleton ShaderData( PFX_DOFCalcCoCShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_CalcCoC_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_CalcCoC_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_CalcCoC_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_CalcCoC_P.glsl"; + + samplerNames[0] = "$shrunkSampler"; + samplerNames[1] = "$blurredSampler"; + + pixVersion = 3.0; +}; + +singleton ShaderData( PFX_DOFSmallBlurShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_SmallBlur_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_SmallBlur_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_SmallBlur_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_SmallBlur_P.glsl"; + + samplerNames[0] = "$colorSampler"; + + pixVersion = 3.0; +}; + +singleton ShaderData( PFX_DOFFinalShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_Final_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/DOF_Final_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_Final_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/dof/gl/DOF_Final_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$smallBlurSampler"; + samplerNames[2] = "$largeBlurSampler"; + samplerNames[3] = "$depthSampler"; + + pixVersion = 3.0; +}; + +//----------------------------------------------------------------------------- +// PostEffects +//----------------------------------------------------------------------------- + +function DOFPostEffect::onAdd( %this ) +{ + // The weighted distribution of CoC value to the three blur textures + // in the order small, medium, large. Most likely you will not need to + // change this value. + %this.setLerpDist( 0.2, 0.3, 0.5 ); + + // Fill out some default values but DOF really should not be turned on + // without actually specifying your own parameters! + %this.autoFocusEnabled = false; + %this.focalDist = 0.0; + %this.nearBlurMax = 0.5; + %this.farBlurMax = 0.5; + %this.minRange = 50; + %this.maxRange = 500; + %this.nearSlope = -5.0; + %this.farSlope = 5.0; +} + +function DOFPostEffect::setLerpDist( %this, %d0, %d1, %d2 ) +{ + %this.lerpScale = -1.0 / %d0 SPC -1.0 / %d1 SPC -1.0 / %d2 SPC 1.0 / %d2; + %this.lerpBias = 1.0 SPC ( 1.0 - %d2 ) / %d1 SPC 1.0 / %d2 SPC ( %d2 - 1.0 ) / %d2; +} + +singleton PostEffect( DOFPostEffect ) +{ + renderTime = "PFXAfterBin"; + renderBin = "GlowBin"; + renderPriority = 0.1; + + shader = PFX_DOFDownSampleShader; + stateBlock = PFX_DOFDownSampleStateBlock; + texture[0] = "$backBuffer"; + texture[1] = "#prepass"; + target = "#shrunk"; + targetScale = "0.25 0.25"; + + isEnabled = false; +}; + +singleton PostEffect( DOFBlurY ) +{ + shader = PFX_DOFBlurYShader; + stateBlock = PFX_DOFBlurStateBlock; + texture[0] = "#shrunk"; + target = "$outTex"; +}; + +DOFPostEffect.add( DOFBlurY ); + +singleton PostEffect( DOFBlurX ) +{ + shader = PFX_DOFBlurXShader; + stateBlock = PFX_DOFBlurStateBlock; + texture[0] = "$inTex"; + target = "#largeBlur"; +}; + +DOFPostEffect.add( DOFBlurX ); + +singleton PostEffect( DOFCalcCoC ) +{ + shader = PFX_DOFCalcCoCShader; + stateBlock = PFX_DOFCalcCoCStateBlock; + texture[0] = "#shrunk"; + texture[1] = "#largeBlur"; + target = "$outTex"; +}; + +DOFPostEffect.add( DOFCalcCoc ); + +singleton PostEffect( DOFSmallBlur ) +{ + shader = PFX_DOFSmallBlurShader; + stateBlock = PFX_DefaultDOFStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; +}; + +DOFPostEffect.add( DOFSmallBlur ); + +singleton PostEffect( DOFFinalPFX ) +{ + shader = PFX_DOFFinalShader; + stateBlock = PFX_DOFFinalStateBlock; + texture[0] = "$backBuffer"; + texture[1] = "$inTex"; + texture[2] = "#largeBlur"; + texture[3] = "#prepass"; + target = "$backBuffer"; +}; + +DOFPostEffect.add( DOFFinalPFX ); + + +//----------------------------------------------------------------------------- +// Scripts +//----------------------------------------------------------------------------- + +function DOFPostEffect::setShaderConsts( %this ) +{ + if ( %this.autoFocusEnabled ) + %this.autoFocus(); + + %fd = %this.focalDist / $Param::FarDist; + + %range = mLerp( %this.minRange, %this.maxRange, %fd ) / $Param::FarDist * 0.5; + + // We work in "depth" space rather than real-world units for the + // rest of this method... + + // Given the focal distance and the range around it we want in focus + // we can determine the near-end-distance and far-start-distance + + %ned = getMax( %fd - %range, 0.0 ); + %fsd = getMin( %fd + %range, 1.0 ); + + // near slope + %nsl = %this.nearSlope; + + // Given slope of near blur equation and the near end dist and amount (x2,y2) + // solve for the y-intercept + // y = mx + b + // so... + // y - mx = b + + %b = 0.0 - %nsl * %ned; + + %eqNear = %nsl SPC %b SPC 0.0; + + // Do the same for the far blur equation... + + %fsl = %this.farSlope; + + %b = 0.0 - %fsl * %fsd; + + %eqFar = %fsl SPC %b SPC 1.0; + + %this.setShaderConst( "$dofEqWorld", %eqNear ); + DOFFinalPFX.setShaderConst( "$dofEqFar", %eqFar ); + + %this.setShaderConst( "$maxWorldCoC", %this.nearBlurMax ); + DOFFinalPFX.setShaderConst( "$maxFarCoC", %this.farBlurMax ); + + DOFFinalPFX.setShaderConst( "$dofLerpScale", %this.lerpScale ); + DOFFinalPFX.setShaderConst( "$dofLerpBias", %this.lerpBias ); +} + +function DOFPostEffect::autoFocus( %this ) +{ + if ( !isObject( ServerConnection ) || + !isObject( ServerConnection.getCameraObject() ) ) + { + return; + } + + %mask = $TypeMasks::StaticObjectType | $TypeMasks::TerrainObjectType; + %control = ServerConnection.getCameraObject(); + + %fvec = %control.getEyeVector(); + %start = %control.getEyePoint(); + + %end = VectorAdd( %start, VectorScale( %fvec, $Param::FarDist ) ); + + // Use the client container for this ray cast. + %result = containerRayCast( %start, %end, %mask, %control, true ); + + %hitPos = getWords( %result, 1, 3 ); + + if ( %hitPos $= "" ) + %focDist = $Param::FarDist; + else + %focDist = VectorDist( %hitPos, %start ); + + // For debuging + //$DOF::debug_dist = %focDist; + //$DOF::debug_depth = %focDist / $Param::FarDist; + //echo( "F: " @ %focDist SPC "D: " @ %delta ); + + %this.focalDist = %focDist; +} + + +// For debugging +/* +function reloadDOF() +{ + exec( "./dof.cs" ); + DOFPostEffect.reload(); + DOFPostEffect.disable(); + DOFPostEffect.enable(); +} + +function dofMetricsCallback() +{ + return " | DOF |" @ + " Dist: " @ $DOF::debug_dist @ + " Depth: " @ $DOF::debug_depth; +} +*/ \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/edgeAA.cs b/Templates/BaseGame/game/data/postFX/scripts/client/edgeAA.cs new file mode 100644 index 000000000..4a5014c62 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/edgeAA.cs @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +singleton GFXStateBlockData( PFX_DefaultEdgeAAStateBlock ) +{ + zDefined = true; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + //samplerStates[1] = SamplerWrapPoint; +}; + +singleton ShaderData( PFX_EdgeAADetectShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/edgeDetectP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/gl/edgeDetectP.glsl"; + + samplerNames[0] = "$prepassBuffer"; + + pixVersion = 3.0; +}; + +singleton ShaderData( PFX_EdgeAAShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/edgeAAV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/edgeAAP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/gl/edgeAAV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/gl/edgeAAP.glsl"; + + samplerNames[0] = "$edgeBuffer"; + samplerNames[1] = "$backBuffer"; + + pixVersion = 3.0; +}; + +singleton ShaderData( PFX_EdgeAADebugShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/dbgEdgeDisplayP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl"; + + samplerNames[0] = "$edgeBuffer"; + + pixVersion = 3.0; +}; + +singleton PostEffect( EdgeDetectPostEffect ) +{ + renderTime = "PFXBeforeBin"; + renderBin = "ObjTranslucentBin"; + //renderPriority = 0.1; + targetScale = "0.5 0.5"; + + shader = PFX_EdgeAADetectShader; + stateBlock = PFX_DefaultEdgeAAStateBlock; + texture[0] = "#prepass"; + target = "#edge"; + + isEnabled = true; +}; + +singleton PostEffect( EdgeAAPostEffect ) +{ + renderTime = "PFXAfterDiffuse"; + //renderBin = "ObjTranslucentBin"; + //renderPriority = 0.1; + + shader = PFX_EdgeAAShader; + stateBlock = PFX_DefaultEdgeAAStateBlock; + texture[0] = "#edge"; + texture[1] = "$backBuffer"; + target = "$backBuffer"; +}; + +singleton PostEffect( Debug_EdgeAAPostEffect ) +{ + renderTime = "PFXAfterDiffuse"; + //renderBin = "ObjTranslucentBin"; + //renderPriority = 0.1; + + shader = PFX_EdgeAADebugShader; + stateBlock = PFX_DefaultEdgeAAStateBlock; + texture[0] = "#edge"; + target = "$backBuffer"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/flash.cs b/Templates/BaseGame/game/data/postFX/scripts/client/flash.cs new file mode 100644 index 000000000..ff82c37ee --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/flash.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton ShaderData( PFX_FlashShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/flashP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/flashP.glsl"; + + samplerNames[0] = "$backBuffer"; + + defines = "WHITE_COLOR=float4(1.0,1.0,1.0,0.0);MUL_COLOR=float4(1.0,0.25,0.25,0.0)"; + + pixVersion = 2.0; +}; + +singleton PostEffect( FlashFx ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + + shader = PFX_FlashShader; + texture[0] = "$backBuffer"; + renderPriority = 10; + stateBlock = PFX_DefaultStateBlock; +}; + +function FlashFx::setShaderConsts( %this ) +{ + if ( isObject( ServerConnection ) ) + { + %this.setShaderConst( "$damageFlash", ServerConnection.getDamageFlash() ); + %this.setShaderConst( "$whiteOut", ServerConnection.getWhiteOut() ); + } + else + { + %this.setShaderConst( "$damageFlash", 0 ); + %this.setShaderConst( "$whiteOut", 0 ); + } +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/fog.cs b/Templates/BaseGame/game/data/postFX/scripts/client/fog.cs new file mode 100644 index 000000000..ea1bd805d --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/fog.cs @@ -0,0 +1,135 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Fog +//------------------------------------------------------------------------------ + +singleton ShaderData( FogPassShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/fogP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/fogP.glsl"; + + samplerNames[0] = "$prepassTex"; + + pixVersion = 2.0; +}; + + +singleton GFXStateBlockData( FogPassStateBlock : PFX_DefaultStateBlock ) +{ + blendDefined = true; + blendEnable = true; + blendSrc = GFXBlendSrcAlpha; + blendDest = GFXBlendInvSrcAlpha; +}; + + +singleton PostEffect( FogPostFx ) +{ + // We forward render the reflection pass + // so it does its own fogging. + allowReflectPass = false; + + renderTime = "PFXBeforeBin"; + renderBin = "ObjTranslucentBin"; + + shader = FogPassShader; + stateBlock = FogPassStateBlock; + texture[0] = "#prepass"; + + renderPriority = 5; + + targetFormat = getBestHDRFormat(); + isEnabled = true; +}; + + +//------------------------------------------------------------------------------ +// UnderwaterFog +//------------------------------------------------------------------------------ + +singleton ShaderData( UnderwaterFogPassShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/underwaterFogP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/underwaterFogP.glsl"; + + samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$backbuffer"; + samplerNames[2] = "$waterDepthGradMap"; + + pixVersion = 2.0; +}; + + +singleton GFXStateBlockData( UnderwaterFogPassStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampPoint; + samplerStates[2] = SamplerClampLinear; +}; + + +singleton PostEffect( UnderwaterFogPostFx ) +{ + oneFrameOnly = true; + onThisFrame = false; + + // Let the fog effect render during the + // reflection pass. + allowReflectPass = true; + + renderTime = "PFXBeforeBin"; + renderBin = "ObjTranslucentBin"; + + shader = UnderwaterFogPassShader; + stateBlock = UnderwaterFogPassStateBlock; + texture[0] = "#prepass"; + texture[1] = "$backBuffer"; + texture[2] = "#waterDepthGradMap"; + + // Needs to happen after the FogPostFx + renderPriority = 4; + + isEnabled = true; +}; + +function UnderwaterFogPostFx::onEnabled( %this ) +{ + TurbulenceFx.enable(); + CausticsPFX.enable(); + return true; +} + +function UnderwaterFogPostFx::onDisabled( %this ) +{ + TurbulenceFx.disable(); + CausticsPFX.disable(); + return false; +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/fxaa.cs b/Templates/BaseGame/game/data/postFX/scripts/client/fxaa.cs new file mode 100644 index 000000000..3f741a072 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/fxaa.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "NVIDIA FXAA 3.11" by TIMOTHY LOTTES +// +// http://timothylottes.blogspot.com/ +// +// The shader is tuned for the defaul quality and good performance. +// See shaders\common\postFx\fxaa\fxaaP.hlsl to tweak the internal +// quality and performance settings. + +singleton GFXStateBlockData( FXAA_StateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +singleton ShaderData( FXAA_ShaderData ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/fxaa/fxaaV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/fxaa/fxaaP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/fxaa/gl/fxaaV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/fxaa/gl/fxaaP.glsl"; + + samplerNames[0] = "$colorTex"; + + pixVersion = 3.0; +}; + +singleton PostEffect( FXAA_PostEffect ) +{ + isEnabled = false; + + allowReflectPass = false; + renderTime = "PFXAfterDiffuse"; + + texture[0] = "$backBuffer"; + + target = "$backBuffer"; + + stateBlock = FXAA_StateBlock; + shader = FXAA_ShaderData; +}; + diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/glow.cs b/Templates/BaseGame/game/data/postFX/scripts/client/glow.cs new file mode 100644 index 000000000..3314aa97a --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/glow.cs @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +singleton ShaderData( PFX_GlowBlurVertShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/glowBlurV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/glowBlurP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/gl/glowBlurV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/glowBlurP.glsl"; + + defines = "BLUR_DIR=float2(0.0,1.0)"; + + samplerNames[0] = "$diffuseMap"; + + pixVersion = 2.0; +}; + + +singleton ShaderData( PFX_GlowBlurHorzShader : PFX_GlowBlurVertShader ) +{ + defines = "BLUR_DIR=float2(1.0,0.0)"; +}; + + +singleton GFXStateBlockData( PFX_GlowCombineStateBlock : PFX_DefaultStateBlock ) +{ + // Use alpha test to save some fillrate + // on the non-glowing areas of the scene. + alphaDefined = true; + alphaTestEnable = true; + alphaTestRef = 1; + alphaTestFunc = GFXCmpGreaterEqual; + + // Do a one to one blend. + blendDefined = true; + blendEnable = true; + blendSrc = GFXBlendOne; + blendDest = GFXBlendOne; +}; + + +singleton PostEffect( GlowPostFx ) +{ + // Do not allow the glow effect to work in reflection + // passes by default so we don't do the extra drawing. + allowReflectPass = false; + + renderTime = "PFXAfterBin"; + renderBin = "GlowBin"; + renderPriority = 1; + + // First we down sample the glow buffer. + shader = PFX_PassthruShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "#glowbuffer"; + target = "$outTex"; + targetScale = "0.5 0.5"; + + isEnabled = true; + + // Blur vertically + new PostEffect() + { + shader = PFX_GlowBlurVertShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + }; + + // Blur horizontally + new PostEffect() + { + shader = PFX_GlowBlurHorzShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + }; + + // Upsample and combine with the back buffer. + new PostEffect() + { + shader = PFX_PassthruShader; + stateBlock = PFX_GlowCombineStateBlock; + texture[0] = "$inTex"; + target = "$backBuffer"; + }; +}; + +singleton ShaderData( PFX_VolFogGlowBlurVertShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/glowBlurV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/VolFogGlowP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/gl/glowBlurV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/VolFogGlowP.glsl"; + + defines = "BLUR_DIR=float2(0.0,1.0)"; + samplerNames[0] = "$diffuseMap"; + pixVersion = 2.0; +}; +singleton ShaderData( PFX_VolFogGlowBlurHorzShader : PFX_VolFogGlowBlurVertShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/glowBlurV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/VolFogGlowP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/gl/glowBlurV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/VolFogGlowP.glsl"; + + defines = "BLUR_DIR=float2(1.0,0.0)"; +}; + +$VolFogGlowPostFx::glowStrength = 0.3; + +singleton PostEffect( VolFogGlowPostFx ) +{ + // Do not allow the glow effect to work in reflection + // passes by default so we don't do the extra drawing. + allowReflectPass = false; + renderTime = "PFXAfterBin"; + renderBin = "FogBin"; + renderPriority = 1; + // First we down sample the glow buffer. + shader = PFX_PassthruShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$backbuffer"; + target = "$outTex"; + targetScale = "0.5 0.5"; + isEnabled = true; + // Blur vertically + new PostEffect() + { + shader = PFX_VolFogGlowBlurVertShader; + stateBlock = PFX_DefaultStateBlock; + internalName = "vert"; + texture[0] = "$inTex"; + target = "$outTex"; + }; + // Blur horizontally + new PostEffect() + { + shader = PFX_VolFogGlowBlurHorzShader; + stateBlock = PFX_DefaultStateBlock; + internalName = "hor"; + texture[0] = "$inTex"; + target = "$outTex"; + }; + // Upsample and combine with the back buffer. + new PostEffect() + { + shader = PFX_PassthruShader; + stateBlock = PFX_GlowCombineStateBlock; + texture[0] = "$inTex"; + target = "$backBuffer"; + }; +}; + +function VolFogGlowPostFx::setShaderConsts( %this ) +{ + %vp=%this-->vert; + %vp.setShaderConst( "$strength", $VolFogGlowPostFx::glowStrength ); + %vp=%this-->hor; + %vp.setShaderConst( "$strength", $VolFogGlowPostFx::glowStrength ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/hdr.cs b/Templates/BaseGame/game/data/postFX/scripts/client/hdr.cs new file mode 100644 index 000000000..1ccf07145 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/hdr.cs @@ -0,0 +1,533 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +/// Blends between the scene and the tone mapped scene. +$HDRPostFX::enableToneMapping = 0.5; + +/// The tone mapping middle grey or exposure value used +/// to adjust the overall "balance" of the image. +/// +/// 0.18 is fairly common value. +/// +$HDRPostFX::keyValue = 0.18; + +/// The minimum luninace value to allow when tone mapping +/// the scene. Is particularly useful if your scene very +/// dark or has a black ambient color in places. +$HDRPostFX::minLuminace = 0.001; + +/// The lowest luminance value which is mapped to white. This +/// is usually set to the highest visible luminance in your +/// scene. By setting this to smaller values you get a contrast +/// enhancement. +$HDRPostFX::whiteCutoff = 1.0; + +/// The rate of adaptation from the previous and new +/// average scene luminance. +$HDRPostFX::adaptRate = 2.0; + +/// Blends between the scene and the blue shifted version +/// of the scene for a cinematic desaturated night effect. +$HDRPostFX::enableBlueShift = 0.0; + +/// The blue shift color value. +$HDRPostFX::blueShiftColor = "1.05 0.97 1.27"; + + +/// Blends between the scene and the bloomed scene. +$HDRPostFX::enableBloom = 1.0; + +/// The threshold luminace value for pixels which are +/// considered "bright" and need to be bloomed. +$HDRPostFX::brightPassThreshold = 1.0; + +/// These are used in the gaussian blur of the +/// bright pass for the bloom effect. +$HDRPostFX::gaussMultiplier = 0.3; +$HDRPostFX::gaussMean = 0.0; +$HDRPostFX::gaussStdDev = 0.8; + +/// The 1x255 color correction ramp texture used +/// by both the HDR shader and the GammaPostFx shader +/// for doing full screen color correction. +$HDRPostFX::colorCorrectionRamp = "data/postFX/art/null_color_ramp.png"; + + +singleton ShaderData( HDR_BrightPassShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/brightPassFilterP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/brightPassFilterP.glsl"; + + samplerNames[0] = "$inputTex"; + samplerNames[1] = "$luminanceTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_DownScale4x4Shader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/downScale4x4V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/downScale4x4P.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/downScale4x4V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/downScale4x4P.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 2.0; +}; + +singleton ShaderData( HDR_BloomGaussBlurHShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/bloomGaussBlurHP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/bloomGaussBlurHP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_BloomGaussBlurVShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/bloomGaussBlurVP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/bloomGaussBlurVP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_SampleLumShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/sampleLumInitialP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/sampleLumInitialP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_DownSampleLumShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/sampleLumIterativeP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/sampleLumIterativeP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_CalcAdaptedLumShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/calculateAdaptedLumP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/calculateAdaptedLumP.glsl"; + + samplerNames[0] = "$currLum"; + samplerNames[1] = "$lastAdaptedLum"; + + pixVersion = 3.0; +}; + +singleton ShaderData( HDR_CombineShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/finalPassCombineP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/finalPassCombineP.glsl"; + + samplerNames[0] = "$sceneTex"; + samplerNames[1] = "$luminanceTex"; + samplerNames[2] = "$bloomTex"; + samplerNames[3] = "$colorCorrectionTex"; + samplerNames[4] = "prepassTex"; + + pixVersion = 3.0; +}; + + +singleton GFXStateBlockData( HDR_SampleStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampPoint; +}; + +singleton GFXStateBlockData( HDR_DownSampleStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; +}; + +singleton GFXStateBlockData( HDR_CombineStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerClampLinear; + samplerStates[2] = SamplerClampLinear; + samplerStates[3] = SamplerClampLinear; +}; + +singleton GFXStateBlockData( HDRStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; + samplerStates[2] = SamplerClampLinear; + samplerStates[3] = SamplerClampLinear; + + blendDefined = true; + blendDest = GFXBlendOne; + blendSrc = GFXBlendZero; + + zDefined = true; + zEnable = false; + zWriteEnable = false; + + cullDefined = true; + cullMode = GFXCullNone; +}; + + +function HDRPostFX::setShaderConsts( %this ) +{ + %this.setShaderConst( "$brightPassThreshold", $HDRPostFX::brightPassThreshold ); + %this.setShaderConst( "$g_fMiddleGray", $HDRPostFX::keyValue ); + + %bloomH = %this-->bloomH; + %bloomH.setShaderConst( "$gaussMultiplier", $HDRPostFX::gaussMultiplier ); + %bloomH.setShaderConst( "$gaussMean", $HDRPostFX::gaussMean ); + %bloomH.setShaderConst( "$gaussStdDev", $HDRPostFX::gaussStdDev ); + + %bloomV = %this-->bloomV; + %bloomV.setShaderConst( "$gaussMultiplier", $HDRPostFX::gaussMultiplier ); + %bloomV.setShaderConst( "$gaussMean", $HDRPostFX::gaussMean ); + %bloomV.setShaderConst( "$gaussStdDev", $HDRPostFX::gaussStdDev ); + + %minLuminace = $HDRPostFX::minLuminace; + if ( %minLuminace <= 0.0 ) + { + // The min should never be pure zero else the + // log() in the shader will generate INFs. + %minLuminace = 0.00001; + } + %this-->adaptLum.setShaderConst( "$g_fMinLuminace", %minLuminace ); + + %this-->finalLum.setShaderConst( "$adaptRate", $HDRPostFX::adaptRate ); + + %combinePass = %this-->combinePass; + %combinePass.setShaderConst( "$g_fEnableToneMapping", $HDRPostFX::enableToneMapping ); + %combinePass.setShaderConst( "$g_fMiddleGray", $HDRPostFX::keyValue ); + %combinePass.setShaderConst( "$g_fBloomScale", $HDRPostFX::enableBloom ); + %combinePass.setShaderConst( "$g_fEnableBlueShift", $HDRPostFX::enableBlueShift ); + %combinePass.setShaderConst( "$g_fBlueShiftColor", $HDRPostFX::blueShiftColor ); + + %clampedGamma = mClamp( $pref::Video::Gamma, 2.0, 2.5); + %combinePass.setShaderConst( "$g_fOneOverGamma", 1 / %clampedGamma ); + %combinePass.setShaderConst( "$Brightness", $pref::Video::Brightness ); + %combinePass.setShaderConst( "$Contrast", $pref::Video::Contrast ); + + %whiteCutoff = ( $HDRPostFX::whiteCutoff * $HDRPostFX::whiteCutoff ) * + ( $HDRPostFX::whiteCutoff * $HDRPostFX::whiteCutoff ); + %combinePass.setShaderConst( "$g_fWhiteCutoff", %whiteCutoff ); +} + +function HDRPostFX::preProcess( %this ) +{ + %combinePass = %this-->combinePass; + + if ( %combinePass.texture[3] !$= $HDRPostFX::colorCorrectionRamp ) + %combinePass.setTexture( 3, $HDRPostFX::colorCorrectionRamp ); +} + +function HDRPostFX::onEnabled( %this ) +{ + // We don't allow hdr on OSX yet. + if ( $platform $= "macos" ) + return false; + + // See what HDR format would be best. + %format = getBestHDRFormat(); + if ( %format $= "" || %format $= "GFXFormatR8G8B8A8" ) + { + // We didn't get a valid HDR format... so fail. + return false; + } + + // HDR does it's own gamma calculation so + // disable this postFx. + GammaPostFX.disable(); + + // Set the right global shader define for HDR. + if ( %format $= "GFXFormatR10G10B10A2" ) + addGlobalShaderMacro( "TORQUE_HDR_RGB10" ); + else if ( %format $= "GFXFormatR16G16B16A16" ) + addGlobalShaderMacro( "TORQUE_HDR_RGB16" ); + + echo( "HDR FORMAT: " @ %format ); + + // Change the format of the offscreen surface + // to an HDR compatible format. + AL_FormatToken.format = %format; + setReflectFormat( %format ); + + // Reset the light manager which will ensure the new + // hdr encoding takes effect in all the shaders and + // that the offscreen surface is enabled. + resetLightManager(); + + return true; +} + +function HDRPostFX::onDisabled( %this ) +{ + // Enable a special GammaCorrection PostFX when this is disabled. + GammaPostFX.enable(); + + // Restore the non-HDR offscreen surface format. + %format = getBestHDRFormat(); + AL_FormatToken.format = %format; + setReflectFormat( %format ); + + removeGlobalShaderMacro( "TORQUE_HDR_RGB10" ); + removeGlobalShaderMacro( "TORQUE_HDR_RGB16" ); + + // Reset the light manager which will ensure the new + // hdr encoding takes effect in all the shaders. + resetLightManager(); +} + +singleton PostEffect( HDRPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + // Resolve the HDR before we render any editor stuff + // and before we resolve the scene to the backbuffer. + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 9999; + + // The bright pass generates a bloomed version of + // the scene for pixels which are brighter than a + // fixed threshold value. + // + // This is then used in the final HDR combine pass + // at the end of this post effect chain. + // + + shader = HDR_BrightPassShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$backBuffer"; + texture[1] = "#adaptedLum"; + target = "$outTex"; + targetFormat = "GFXFormatR16G16B16A16F"; + targetScale = "0.5 0.5"; + + new PostEffect() + { + allowReflectPass = false; + shader = HDR_DownScale4x4Shader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + targetFormat = "GFXFormatR16G16B16A16F"; + targetScale = "0.25 0.25"; + }; + + new PostEffect() + { + allowReflectPass = false; + internalName = "bloomH"; + + shader = HDR_BloomGaussBlurHShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + targetFormat = "GFXFormatR16G16B16A16F"; + }; + + new PostEffect() + { + allowReflectPass = false; + internalName = "bloomV"; + + shader = HDR_BloomGaussBlurVShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "#bloomFinal"; + targetFormat = "GFXFormatR16G16B16A16F"; + }; + + // BrightPass End + + // Now calculate the adapted luminance. + new PostEffect() + { + allowReflectPass = false; + internalName = "adaptLum"; + + shader = HDR_SampleLumShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$backBuffer"; + target = "$outTex"; + targetScale = "0.0625 0.0625"; // 1/16th + targetFormat = "GFXFormatR16F"; + + new PostEffect() + { + allowReflectPass = false; + shader = HDR_DownSampleLumShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + targetScale = "0.25 0.25"; // 1/4 + targetFormat = "GFXFormatR16F"; + }; + + new PostEffect() + { + allowReflectPass = false; + shader = HDR_DownSampleLumShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + targetScale = "0.25 0.25"; // 1/4 + targetFormat = "GFXFormatR16F"; + }; + + new PostEffect() + { + allowReflectPass = false; + shader = HDR_DownSampleLumShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + target = "$outTex"; + targetScale = "0.25 0.25"; // At this point the target should be 1x1. + targetFormat = "GFXFormatR16F"; + }; + + // Note that we're reading the adapted luminance + // from the previous frame when generating this new + // one... PostEffect takes care to manage that. + new PostEffect() + { + allowReflectPass = false; + internalName = "finalLum"; + shader = HDR_CalcAdaptedLumShader; + stateBlock = HDR_DownSampleStateBlock; + texture[0] = "$inTex"; + texture[1] = "#adaptedLum"; + target = "#adaptedLum"; + targetFormat = "GFXFormatR16F"; + targetClear = "PFXTargetClear_OnCreate"; + targetClearColor = "1 1 1 1"; + }; + }; + + // Output the combined bloom and toned mapped + // version of the scene. + new PostEffect() + { + allowReflectPass = false; + internalName = "combinePass"; + + shader = HDR_CombineShader; + stateBlock = HDR_CombineStateBlock; + texture[0] = "$backBuffer"; + texture[1] = "#adaptedLum"; + texture[2] = "#bloomFinal"; + texture[3] = $HDRPostFX::colorCorrectionRamp; + target = "$backBuffer"; + }; +}; + +singleton ShaderData( LuminanceVisShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/luminanceVisP.hlsl"; + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/hdr/gl/luminanceVisP.glsl"; + + samplerNames[0] = "$inputTex"; + + pixVersion = 3.0; +}; + +singleton GFXStateBlockData( LuminanceVisStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +function LuminanceVisPostFX::setShaderConsts( %this ) +{ + %this.setShaderConst( "$brightPassThreshold", $HDRPostFX::brightPassThreshold ); +} + +singleton PostEffect( LuminanceVisPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + // Render before we do any editor rendering. + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 9999; + + shader = LuminanceVisShader; + stateBlock = LuminanceVisStateBlock; + texture[0] = "$backBuffer"; + target = "$backBuffer"; + //targetScale = "0.0625 0.0625"; // 1/16th + //targetFormat = "GFXFormatR16F"; +}; + +function LuminanceVisPostFX::onEnabled( %this ) +{ + if ( !HDRPostFX.isEnabled() ) + { + HDRPostFX.enable(); + } + + HDRPostFX.skip = true; + + return true; +} + +function LuminanceVisPostFX::onDisabled( %this ) +{ + HDRPostFX.skip = false; +} + diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/lightRay.cs b/Templates/BaseGame/game/data/postFX/scripts/client/lightRay.cs new file mode 100644 index 000000000..14c17885c --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/lightRay.cs @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +$LightRayPostFX::brightScalar = 0.75; +$LightRayPostFX::numSamples = 40; +$LightRayPostFX::density = 0.94; +$LightRayPostFX::weight = 5.65; +$LightRayPostFX::decay = 1.0; +$LightRayPostFX::exposure = 0.0005; +$LightRayPostFX::resolutionScale = 1.0; + + +singleton ShaderData( LightRayOccludeShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/lightRay/lightRayOccludeP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/lightRay/gl/lightRayOccludeP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; + + pixVersion = 3.0; +}; + +singleton ShaderData( LightRayShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/lightRay/lightRayP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/lightRay/gl/lightRayP.glsl"; + + samplerNames[0] = "$frameSampler"; + samplerNames[1] = "$backBuffer"; + + pixVersion = 3.0; +}; + +singleton GFXStateBlockData( LightRayStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampLinear; +}; + +singleton PostEffect( LightRayPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXBeforeBin"; + renderBin = "EditorBin"; + renderPriority = 10; + + shader = LightRayOccludeShader; + stateBlock = LightRayStateBlock; + texture[0] = "$backBuffer"; + texture[1] = "#prepass"; + target = "$outTex"; + targetFormat = "GFXFormatR16G16B16A16F"; + + new PostEffect() + { + shader = LightRayShader; + stateBlock = LightRayStateBlock; + internalName = "final"; + texture[0] = "$inTex"; + texture[1] = "$backBuffer"; + target = "$backBuffer"; + }; +}; + +function LightRayPostFX::preProcess( %this ) +{ + %this.targetScale = $LightRayPostFX::resolutionScale SPC $LightRayPostFX::resolutionScale; +} + +function LightRayPostFX::setShaderConsts( %this ) +{ + %this.setShaderConst( "$brightScalar", $LightRayPostFX::brightScalar ); + + %pfx = %this-->final; + %pfx.setShaderConst( "$numSamples", $LightRayPostFX::numSamples ); + %pfx.setShaderConst( "$density", $LightRayPostFX::density ); + %pfx.setShaderConst( "$weight", $LightRayPostFX::weight ); + %pfx.setShaderConst( "$decay", $LightRayPostFX::decay ); + %pfx.setShaderConst( "$exposure", $LightRayPostFX::exposure ); +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/ovrBarrelDistortion.cs b/Templates/BaseGame/game/data/postFX/scripts/client/ovrBarrelDistortion.cs new file mode 100644 index 000000000..bffde5fce --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/ovrBarrelDistortion.cs @@ -0,0 +1,167 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Only load these shaders if an Oculus VR device is present +if(!isFunction(isOculusVRDeviceActive)) + return; + +//----------------------------------------------------------------------------- +// Shader data +//----------------------------------------------------------------------------- + +singleton ShaderData( OVRMonoToStereoShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/oculusvr/monoToStereoP.hlsl"; + + //OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/gl/postFxV.hlsl"; + //OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/oculusvr/gl/monoToStereoP.glsl"; + + samplerNames[0] = "$backBuffer"; + + pixVersion = 2.0; +}; + +singleton ShaderData( OVRBarrelDistortionShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/oculusvr/barrelDistortionP.hlsl"; + + //OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/gl/postFxV.glsl"; + //OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/oculusvr/gl/barrelDistortionP.glsl"; + + samplerNames[0] = "$backBuffer"; + + pixVersion = 2.0; +}; + +singleton ShaderData( OVRBarrelDistortionChromaShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/oculusvr/barrelDistortionChromaP.hlsl"; + + pixVersion = 2.0; +}; + +//----------------------------------------------------------------------------- +// GFX state blocks +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( OVRBarrelDistortionStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion PostFx +// +// To be used with the Oculus Rift. +// Expects a stereo pair to exist on the back buffer and then applies the +// appropriate barrel distortion. +//----------------------------------------------------------------------------- +singleton BarrelDistortionPostEffect( OVRBarrelDistortionPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // The barrel distortion + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + + scaleOutput = 1.25; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion with Chromatic Aberration Correction PostFx +// +// To be used with the Oculus Rift. +// Expects a stereo pair to exist on the back buffer and then applies the +// appropriate barrel distortion. +// This version applies a chromatic aberration correction during the +// barrel distortion. +//----------------------------------------------------------------------------- +singleton BarrelDistortionPostEffect( OVRBarrelDistortionChromaPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // The barrel distortion + shader = OVRBarrelDistortionChromaShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + + scaleOutput = 1.25; +}; + +//----------------------------------------------------------------------------- +// Barrel Distortion Mono PostFx +// +// To be used with the Oculus Rift. +// Takes a non-stereo image and turns it into a stereo pair with barrel +// distortion applied. Only a vertical slice around the center of the back +// buffer is used to generate the pseudo stereo pair. +//----------------------------------------------------------------------------- +singleton PostEffect( OVRBarrelDistortionMonoPostFX ) +{ + isEnabled = false; + allowReflectPass = false; + + renderTime = "PFXAfterDiffuse"; + renderPriority = 100; + + // Converts the mono display to a stereo one + shader = OVRMonoToStereoShader; + stateBlock = OVRBarrelDistortionStateBlock; + + texture[0] = "$backBuffer"; + target = "$outTex"; + + // The actual barrel distortion + new BarrelDistortionPostEffect(OVRBarrelDistortionMonoStage2PostFX) + { + shader = OVRBarrelDistortionShader; + stateBlock = OVRBarrelDistortionStateBlock; + texture[0] = "$inTex"; + target = "$backBuffer"; + + scaleOutput = 1.25; + }; + +}; + +function OVRBarrelDistortionMonoPostFX::setShaderConsts( %this ) +{ + %HMDIndex = 0; + + %xOffsets = getOVRHMDEyeXOffsets(%HMDIndex); + %this.setShaderConst( "$LensXOffsets", %xOffsets ); +} diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.cs b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.cs new file mode 100644 index 000000000..8c1c8e8c7 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.cs @@ -0,0 +1,446 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$PostFXManager::vebose = true; +function postVerbose(%string) +{ + if($PostFXManager::vebose == true) + { + echo(%string); + } +} + +function PostFXManager::onDialogPush( %this ) +{ + //Apply the settings to the controls + postVerbose("% - PostFX Manager - Loading GUI."); + + %this.settingsRefreshAll(); +} + +// :: Controls for the overall postFX manager dialog +function ppOptionsEnable::onAction(%this) +{ + //Disable / Enable all PostFX + + if(ppOptionsEnable.getValue()) + { + %toEnable = true; + } + else + { + %toEnable = false; + } + + PostFXManager.settingsSetEnabled(%toEnable); + +} + +function PostFXManager::getEnableResultFromControl(%this, %control) +{ + %toEnable = -1; + %bTest = %control.getValue(); + if(%bTest == 1) + { + %toEnable = true; + } + else + { + %toEnable = false; + } + + return %toEnable; +} + +function ppOptionsEnableSSAO::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("SSAO", %toEnable); +} + +function ppOptionsEnableHDR::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("HDR", %toEnable); +} + +function ppOptionsEnableLightRays::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("LightRays", %toEnable); +} + +function ppOptionsEnableDOF::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("DOF", %toEnable); +} + +function ppOptionsEnableVignette::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("Vignette", %toEnable); +} + +function ppOptionsSavePreset::onClick(%this) +{ + //Stores the current settings into a preset file for loading and use later on +} + +function ppOptionsLoadPreset::onClick(%this) +{ + //Loads and applies the settings from a postfxpreset file +} + + +//Other controls, Quality dropdown +function ppOptionsSSAOQuality::onSelect( %this, %id, %text ) +{ + if(%id > -1 && %id < 3) + { + $SSAOPostFx::quality = %id; + } +} + +//SSAO Slider controls +//General Tab +function ppOptionsSSAOOverallStrength::onMouseDragged(%this) +{ + $SSAOPostFx::overallStrength = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAOBlurDepth::onMouseDragged(%this) +{ + $SSAOPostFx::blurDepthTol = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAOBlurNormal::onMouseDragged(%this) +{ + $SSAOPostFx::blurNormalTol = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +//Near Tab +function ppOptionsSSAONearRadius::onMouseDragged(%this) +{ + $SSAOPostFx::sRadius = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAONearStrength::onMouseDragged(%this) +{ + $SSAOPostFx::sStrength = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAONearDepthMin::onMouseDragged(%this) +{ + $SSAOPostFx::sDepthMin = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAONearDepthMax::onMouseDragged(%this) +{ + $SSAOPostFx::sDepthMax = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAONearToleranceNormal::onMouseDragged(%this) +{ + $SSAOPostFx::sNormalTol = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsSSAONearTolerancePower::onMouseDragged(%this) +{ + $SSAOPostFx::sNormalPow = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +//Far Tab +function ppOptionsSSAOFarRadius::onMouseDragged(%this) +{ + $SSAOPostFx::lRadius = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +function ppOptionsSSAOFarStrength::onMouseDragged(%this) +{ + $SSAOPostFx::lStrength = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +function ppOptionsSSAOFarDepthMin::onMouseDragged(%this) +{ + $SSAOPostFx::lDepthMin = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +function ppOptionsSSAOFarDepthMax::onMouseDragged(%this) +{ + $SSAOPostFx::lDepthMax = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +function ppOptionsSSAOFarToleranceNormal::onMouseDragged(%this) +{ + $SSAOPostFx::lNormalTol = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +function ppOptionsSSAOFarTolerancePower::onMouseDragged(%this) +{ + $SSAOPostFx::lNormalPow = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +//HDR Slider Controls +//Brighness tab + +function ppOptionsHDRToneMappingAmount::onMouseDragged(%this) +{ + + $HDRPostFX::enableToneMapping = %this.value; + %this.ToolTip = "value : " @ %this.value; +} + +function ppOptionsHDRKeyValue::onMouseDragged(%this) +{ + $HDRPostFX::keyValue = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRMinLuminance::onMouseDragged(%this) +{ + $HDRPostFX::minLuminace = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRWhiteCutoff::onMouseDragged(%this) +{ + $HDRPostFX::whiteCutoff = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRBrightnessAdaptRate::onMouseDragged(%this) +{ + $HDRPostFX::adaptRate = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +//Blur tab +function ppOptionsHDRBloomBlurBrightPassThreshold::onMouseDragged(%this) +{ + $HDRPostFX::brightPassThreshold = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRBloomBlurMultiplier::onMouseDragged(%this) +{ + $HDRPostFX::gaussMultiplier = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRBloomBlurMean::onMouseDragged(%this) +{ + $HDRPostFX::gaussMean = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRBloomBlurStdDev::onMouseDragged(%this) +{ + $HDRPostFX::gaussStdDev = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + +function ppOptionsHDRBloom::onAction(%this) +{ + $HDRPostFX::enableBloom = %this.getValue(); +} + +function ppOptionsHDRToneMapping::onAction(%this) +{ + //$HDRPostFX::enableToneMapping = %this.getValue(); +} + +function ppOptionsHDREffectsBlueShift::onAction(%this) +{ + $HDRPostFX::enableBlueShift = %this.getValue(); +} + + +//Controls for color range in blue Shift dialog + +function ppOptionsHDREffectsBlueShiftColorBlend::onAction(%this) +{ + $HDRPostFX::blueShiftColor = %this.PickColor; + %this.ToolTip = "Color Values : " @ %this.PickColor; +} + +function ppOptionsHDREffectsBlueShiftColorBaseColor::onAction(%this) +{ + //This one feeds the one above + ppOptionsHDREffectsBlueShiftColorBlend.baseColor = %this.PickColor; + %this.ToolTip = "Color Values : " @ %this.PickColor; +} + + +//Light rays Brightness Slider Controls +function ppOptionsLightRaysBrightScalar::onMouseDragged(%this) +{ + $LightRayPostFX::brightScalar = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +//Light rays Number of Samples Slider Control +function ppOptionsLightRaysSampleScalar::onMouseDragged(%this) +{ + $LightRayPostFX::numSamples = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +//Light rays Density Slider Control +function ppOptionsLightRaysDensityScalar::onMouseDragged(%this) +{ + $LightRayPostFX::density = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +//Light rays Weight Slider Control +function ppOptionsLightRaysWeightScalar::onMouseDragged(%this) +{ + $LightRayPostFX::weight = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} +//Light rays Decay Slider Control +function ppOptionsLightRaysDecayScalar::onMouseDragged(%this) +{ + $LightRayPostFX::decay = %this.value; + %this.ToolTip = "Value : " @ %this.value; +} + + +function ppOptionsUpdateDOFSettings() +{ + DOFPostEffect.setFocusParams( $DOFPostFx::BlurMin, $DOFPostFx::BlurMax, $DOFPostFx::FocusRangeMin, $DOFPostFx::FocusRangeMax, -($DOFPostFx::BlurCurveNear), $DOFPostFx::BlurCurveFar ); + + DOFPostEffect.setAutoFocus( $DOFPostFx::EnableAutoFocus ); + DOFPostEffect.setFocalDist(0); + + if($PostFXManager::PostFX::EnableDOF) + { + DOFPostEffect.enable(); + } + else + { + DOFPostEffect.disable(); + } +} + +//DOF General Tab +//DOF Toggles +function ppOptionsDOFEnableDOF::onAction(%this) +{ + $PostFXManager::PostFX::EnableDOF = %this.getValue(); + ppOptionsUpdateDOFSettings(); +} + + +function ppOptionsDOFEnableAutoFocus::onAction(%this) +{ + $DOFPostFx::EnableAutoFocus = %this.getValue(); + DOFPostEffect.setAutoFocus( %this.getValue() ); +} + +//DOF AutoFocus Slider controls +function ppOptionsDOFFarBlurMinSlider::onMouseDragged(%this) +{ + $DOFPostFx::BlurMin = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsDOFFarBlurMaxSlider::onMouseDragged(%this) +{ + $DOFPostFx::BlurMax = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsDOFFocusRangeMinSlider::onMouseDragged(%this) +{ + $DOFPostFx::FocusRangeMin = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsDOFFocusRangeMaxSlider::onMouseDragged(%this) +{ + $DOFPostFx::FocusRangeMax = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsDOFBlurCurveNearSlider::onMouseDragged(%this) +{ + $DOFPostFx::BlurCurveNear = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsDOFBlurCurveFarSlider::onMouseDragged(%this) +{ + $DOFPostFx::BlurCurveFar = %this.value; + ppOptionsUpdateDOFSettings(); +} + +function ppOptionsEnableHDRDebug::onAction(%this) +{ + if ( %this.getValue() ) + LuminanceVisPostFX.enable(); + else + LuminanceVisPostFX.disable(); +} + +function ppOptionsUpdateVignetteSettings() +{ + if($PostFXManager::PostFX::EnableVignette) + { + VignettePostEffect.enable(); + } + else + { + VignettePostEffect.disable(); + } +} + +function ppOptionsVignetteEnableVignette::onAction(%this) +{ + $PostFXManager::PostFX::EnableVignette = %this.getValue(); + ppOptionsUpdateVignetteSettings(); +} + +function ppColorCorrection_selectFile() +{ + %filter = "Image Files (*.png, *.jpg, *.dds, *.bmp, *.gif, *.jng. *.tga)|*.png;*.jpg;*.dds;*.bmp;*.gif;*.jng;*.tga|All Files (*.*)|*.*|"; + getLoadFilename( %filter, "ppColorCorrection_selectFileHandler"); +} + +function ppColorCorrection_selectFileHandler( %filename ) +{ + if ( %filename $= "" || !isFile( %filename ) ) + %filename = "data/postFX/art/null_color_ramp.png"; + else + %filename = makeRelativePath( %filename, getMainDotCsDir() ); + + $HDRPostFX::colorCorrectionRamp = %filename; + PostFXManager-->ColorCorrectionFileName.Text = %filename; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.settings.cs b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.settings.cs new file mode 100644 index 000000000..bc28edc70 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.gui.settings.cs @@ -0,0 +1,439 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$PostFXManager::defaultPreset = "data/postFX/scripts/client/default.postfxpreset.cs"; + +function PostFXManager::settingsSetEnabled(%this, %bEnablePostFX) +{ + $PostFXManager::PostFX::Enabled = %bEnablePostFX; + + //if to enable the postFX, apply the ones that are enabled + if ( %bEnablePostFX ) + { + //SSAO, HDR, LightRays, DOF + + if ( $PostFXManager::PostFX::EnableSSAO ) + SSAOPostFx.enable(); + else + SSAOPostFx.disable(); + + if ( $PostFXManager::PostFX::EnableHDR ) + HDRPostFX.enable(); + else + HDRPostFX.disable(); + + if ( $PostFXManager::PostFX::EnableLightRays ) + LightRayPostFX.enable(); + else + LightRayPostFX.disable(); + + if ( $PostFXManager::PostFX::EnableDOF ) + DOFPostEffect.enable(); + else + DOFPostEffect.disable(); + + if ( $PostFXManager::PostFX::EnableVignette ) + VignettePostEffect.enable(); + else + VignettePostEffect.disable(); + + postVerbose("% - PostFX Manager - PostFX enabled"); + } + else + { + //Disable all postFX + + SSAOPostFx.disable(); + HDRPostFX.disable(); + LightRayPostFX.disable(); + DOFPostEffect.disable(); + VignettePostEffect.disable(); + + postVerbose("% - PostFX Manager - PostFX disabled"); + } + + VolFogGlowPostFx.disable(); +} + +function PostFXManager::settingsEffectSetEnabled(%this, %sName, %bEnable) +{ + %postEffect = 0; + + //Determine the postFX to enable, and apply the boolean + if(%sName $= "SSAO") + { + %postEffect = SSAOPostFx; + $PostFXManager::PostFX::EnableSSAO = %bEnable; + //$pref::PostFX::SSAO::Enabled = %bEnable; + } + else if(%sName $= "HDR") + { + %postEffect = HDRPostFX; + $PostFXManager::PostFX::EnableHDR = %bEnable; + //$pref::PostFX::HDR::Enabled = %bEnable; + } + else if(%sName $= "LightRays") + { + %postEffect = LightRayPostFX; + $PostFXManager::PostFX::EnableLightRays = %bEnable; + //$pref::PostFX::LightRays::Enabled = %bEnable; + } + else if(%sName $= "DOF") + { + %postEffect = DOFPostEffect; + $PostFXManager::PostFX::EnableDOF = %bEnable; + //$pref::PostFX::DOF::Enabled = %bEnable; + } + else if(%sName $= "Vignette") + { + %postEffect = VignettePostEffect; + $PostFXManager::PostFX::EnableVignette = %bEnable; + //$pref::PostFX::Vignette::Enabled = %bEnable; + } + + // Apply the change + if ( %bEnable == true ) + { + %postEffect.enable(); + postVerbose("% - PostFX Manager - " @ %sName @ " enabled"); + } + else + { + %postEffect.disable(); + postVerbose("% - PostFX Manager - " @ %sName @ " disabled"); + } +} + +function PostFXManager::settingsRefreshSSAO(%this) +{ + //Apply the enabled flag + ppOptionsEnableSSAO.setValue($PostFXManager::PostFX::EnableSSAO); + + //Add the items we need to display + ppOptionsSSAOQuality.clear(); + ppOptionsSSAOQuality.add("Low", 0); + ppOptionsSSAOQuality.add("Medium", 1); + ppOptionsSSAOQuality.add("High", 2); + + //Set the selected, after adding the items! + ppOptionsSSAOQuality.setSelected($SSAOPostFx::quality); + + //SSAO - Set the values of the sliders, General Tab + ppOptionsSSAOOverallStrength.setValue($SSAOPostFx::overallStrength); + ppOptionsSSAOBlurDepth.setValue($SSAOPostFx::blurDepthTol); + ppOptionsSSAOBlurNormal.setValue($SSAOPostFx::blurNormalTol); + + //SSAO - Set the values for the near tab + ppOptionsSSAONearDepthMax.setValue($SSAOPostFx::sDepthMax); + ppOptionsSSAONearDepthMin.setValue($SSAOPostFx::sDepthMin); + ppOptionsSSAONearRadius.setValue($SSAOPostFx::sRadius); + ppOptionsSSAONearStrength.setValue($SSAOPostFx::sStrength); + ppOptionsSSAONearToleranceNormal.setValue($SSAOPostFx::sNormalTol); + ppOptionsSSAONearTolerancePower.setValue($SSAOPostFx::sNormalPow); + + //SSAO - Set the values for the far tab + ppOptionsSSAOFarDepthMax.setValue($SSAOPostFx::lDepthMax); + ppOptionsSSAOFarDepthMin.setValue($SSAOPostFx::lDepthMin); + ppOptionsSSAOFarRadius.setValue($SSAOPostFx::lRadius); + ppOptionsSSAOFarStrength.setValue($SSAOPostFx::lStrength); + ppOptionsSSAOFarToleranceNormal.setValue($SSAOPostFx::lNormalTol); + ppOptionsSSAOFarTolerancePower.setValue($SSAOPostFx::lNormalPow); +} + +function PostFXManager::settingsRefreshHDR(%this) +{ + //Apply the enabled flag + ppOptionsEnableHDR.setValue($PostFXManager::PostFX::EnableHDR); + + ppOptionsHDRBloom.setValue($HDRPostFX::enableBloom); + ppOptionsHDRBloomBlurBrightPassThreshold.setValue($HDRPostFX::brightPassThreshold); + ppOptionsHDRBloomBlurMean.setValue($HDRPostFX::gaussMean); + ppOptionsHDRBloomBlurMultiplier.setValue($HDRPostFX::gaussMultiplier); + ppOptionsHDRBloomBlurStdDev.setValue($HDRPostFX::gaussStdDev); + ppOptionsHDRBrightnessAdaptRate.setValue($HDRPostFX::adaptRate); + ppOptionsHDREffectsBlueShift.setValue($HDRPostFX::enableBlueShift); + ppOptionsHDREffectsBlueShiftColor.BaseColor = $HDRPostFX::blueShiftColor; + ppOptionsHDREffectsBlueShiftColor.PickColor = $HDRPostFX::blueShiftColor; + ppOptionsHDRKeyValue.setValue($HDRPostFX::keyValue); + ppOptionsHDRMinLuminance.setValue($HDRPostFX::minLuminace); + ppOptionsHDRToneMapping.setValue($HDRPostFX::enableToneMapping); + ppOptionsHDRToneMappingAmount.setValue($HDRPostFX::enableToneMapping); + ppOptionsHDRWhiteCutoff.setValue($HDRPostFX::whiteCutoff); + + %this-->ColorCorrectionFileName.Text = $HDRPostFX::colorCorrectionRamp; +} + +function PostFXManager::settingsRefreshLightrays(%this) +{ + //Apply the enabled flag + ppOptionsEnableLightRays.setValue($PostFXManager::PostFX::EnableLightRays); + + ppOptionsLightRaysBrightScalar.setValue($LightRayPostFX::brightScalar); + + ppOptionsLightRaysSampleScalar.setValue($LightRayPostFX::numSamples); + ppOptionsLightRaysDensityScalar.setValue($LightRayPostFX::density); + ppOptionsLightRaysWeightScalar.setValue($LightRayPostFX::weight); + ppOptionsLightRaysDecayScalar.setValue($LightRayPostFX::decay); +} + +function PostFXManager::settingsRefreshDOF(%this) +{ + //Apply the enabled flag + ppOptionsEnableDOF.setValue($PostFXManager::PostFX::EnableDOF); + + + //ppOptionsDOFEnableDOF.setValue($PostFXManager::PostFX::EnableDOF); + ppOptionsDOFEnableAutoFocus.setValue($DOFPostFx::EnableAutoFocus); + + ppOptionsDOFFarBlurMinSlider.setValue($DOFPostFx::BlurMin); + ppOptionsDOFFarBlurMaxSlider.setValue($DOFPostFx::BlurMax); + + ppOptionsDOFFocusRangeMinSlider.setValue($DOFPostFx::FocusRangeMin); + ppOptionsDOFFocusRangeMaxSlider.setValue($DOFPostFx::FocusRangeMax); + + ppOptionsDOFBlurCurveNearSlider.setValue($DOFPostFx::BlurCurveNear); + ppOptionsDOFBlurCurveFarSlider.setValue($DOFPostFx::BlurCurveFar); + +} + +function PostFXManager::settingsRefreshVignette(%this) +{ + //Apply the enabled flag + ppOptionsEnableVignette.setValue($PostFXManager::PostFX::EnableVignette); + +} + +function PostFXManager::settingsRefreshAll(%this) +{ + $PostFXManager::PostFX::Enabled = $pref::enablePostEffects; + $PostFXManager::PostFX::EnableSSAO = SSAOPostFx.isEnabled(); + $PostFXManager::PostFX::EnableHDR = HDRPostFX.isEnabled(); + $PostFXManager::PostFX::EnableLightRays = LightRayPostFX.isEnabled(); + $PostFXManager::PostFX::EnableDOF = DOFPostEffect.isEnabled(); + $PostFXManager::PostFX::EnableVignette = VignettePostEffect.isEnabled(); + + //For all the postFX here, apply the active settings in the system + //to the gui controls. + + %this.settingsRefreshSSAO(); + %this.settingsRefreshHDR(); + %this.settingsRefreshLightrays(); + %this.settingsRefreshDOF(); + %this.settingsRefreshVignette(); + + ppOptionsEnable.setValue($PostFXManager::PostFX::Enabled); + + postVerbose("% - PostFX Manager - GUI values updated."); +} + +function PostFXManager::settingsApplyFromPreset(%this) +{ + postVerbose("% - PostFX Manager - Applying from preset"); + + //SSAO Settings + $SSAOPostFx::blurDepthTol = $PostFXManager::Settings::SSAO::blurDepthTol; + $SSAOPostFx::blurNormalTol = $PostFXManager::Settings::SSAO::blurNormalTol; + $SSAOPostFx::lDepthMax = $PostFXManager::Settings::SSAO::lDepthMax; + $SSAOPostFx::lDepthMin = $PostFXManager::Settings::SSAO::lDepthMin; + $SSAOPostFx::lDepthPow = $PostFXManager::Settings::SSAO::lDepthPow; + $SSAOPostFx::lNormalPow = $PostFXManager::Settings::SSAO::lNormalPow; + $SSAOPostFx::lNormalTol = $PostFXManager::Settings::SSAO::lNormalTol; + $SSAOPostFx::lRadius = $PostFXManager::Settings::SSAO::lRadius; + $SSAOPostFx::lStrength = $PostFXManager::Settings::SSAO::lStrength; + $SSAOPostFx::overallStrength = $PostFXManager::Settings::SSAO::overallStrength; + $SSAOPostFx::quality = $PostFXManager::Settings::SSAO::quality; + $SSAOPostFx::sDepthMax = $PostFXManager::Settings::SSAO::sDepthMax; + $SSAOPostFx::sDepthMin = $PostFXManager::Settings::SSAO::sDepthMin; + $SSAOPostFx::sDepthPow = $PostFXManager::Settings::SSAO::sDepthPow; + $SSAOPostFx::sNormalPow = $PostFXManager::Settings::SSAO::sNormalPow; + $SSAOPostFx::sNormalTol = $PostFXManager::Settings::SSAO::sNormalTol; + $SSAOPostFx::sRadius = $PostFXManager::Settings::SSAO::sRadius; + $SSAOPostFx::sStrength = $PostFXManager::Settings::SSAO::sStrength; + + //HDR settings + $HDRPostFX::adaptRate = $PostFXManager::Settings::HDR::adaptRate; + $HDRPostFX::blueShiftColor = $PostFXManager::Settings::HDR::blueShiftColor; + $HDRPostFX::brightPassThreshold = $PostFXManager::Settings::HDR::brightPassThreshold; + $HDRPostFX::enableBloom = $PostFXManager::Settings::HDR::enableBloom; + $HDRPostFX::enableBlueShift = $PostFXManager::Settings::HDR::enableBlueShift; + $HDRPostFX::enableToneMapping = $PostFXManager::Settings::HDR::enableToneMapping; + $HDRPostFX::gaussMean = $PostFXManager::Settings::HDR::gaussMean; + $HDRPostFX::gaussMultiplier = $PostFXManager::Settings::HDR::gaussMultiplier; + $HDRPostFX::gaussStdDev = $PostFXManager::Settings::HDR::gaussStdDev; + $HDRPostFX::keyValue = $PostFXManager::Settings::HDR::keyValue; + $HDRPostFX::minLuminace = $PostFXManager::Settings::HDR::minLuminace; + $HDRPostFX::whiteCutoff = $PostFXManager::Settings::HDR::whiteCutoff; + $HDRPostFX::colorCorrectionRamp = $PostFXManager::Settings::ColorCorrectionRamp; + + //Light rays settings + $LightRayPostFX::brightScalar = $PostFXManager::Settings::LightRays::brightScalar; + + $LightRayPostFX::numSamples = $PostFXManager::Settings::LightRays::numSamples; + $LightRayPostFX::density = $PostFXManager::Settings::LightRays::density; + $LightRayPostFX::weight = $PostFXManager::Settings::LightRays::weight; + $LightRayPostFX::decay = $PostFXManager::Settings::LightRays::decay; + + //DOF settings + $DOFPostFx::EnableAutoFocus = $PostFXManager::Settings::DOF::EnableAutoFocus; + $DOFPostFx::BlurMin = $PostFXManager::Settings::DOF::BlurMin; + $DOFPostFx::BlurMax = $PostFXManager::Settings::DOF::BlurMax; + $DOFPostFx::FocusRangeMin = $PostFXManager::Settings::DOF::FocusRangeMin; + $DOFPostFx::FocusRangeMax = $PostFXManager::Settings::DOF::FocusRangeMax; + $DOFPostFx::BlurCurveNear = $PostFXManager::Settings::DOF::BlurCurveNear; + $DOFPostFx::BlurCurveFar = $PostFXManager::Settings::DOF::BlurCurveFar; + + //Vignette settings + $VignettePostEffect::VMax = $PostFXManager::Settings::Vignette::VMax; + $VignettePostEffect::VMin = $PostFXManager::Settings::Vignette::VMin; + + if ( $PostFXManager::forceEnableFromPresets ) + { + $PostFXManager::PostFX::Enabled = $PostFXManager::Settings::EnablePostFX; + $PostFXManager::PostFX::EnableDOF = $pref::PostFX::EnableDOF ? $PostFXManager::Settings::EnableDOF : false; + $PostFXManager::PostFX::EnableVignette = $pref::PostFX::EnableVignette ? $PostFXManager::Settings::EnableVignette : false; + $PostFXManager::PostFX::EnableLightRays = $pref::PostFX::EnableLightRays ? $PostFXManager::Settings::EnableLightRays : false; + $PostFXManager::PostFX::EnableHDR = $pref::PostFX::EnableHDR ? $PostFXManager::Settings::EnableHDR : false; + $PostFXManager::PostFX::EnableSSAO = $pref::PostFX::EnabledSSAO ? $PostFXManager::Settings::EnableSSAO : false; + + %this.settingsSetEnabled( true ); + } + + //make sure we apply the correct settings to the DOF + ppOptionsUpdateDOFSettings(); + + // Update the actual GUI controls if its awake ( otherwise it will when opened ). + if ( PostFXManager.isAwake() ) + %this.settingsRefreshAll(); +} + +function PostFXManager::settingsApplySSAO(%this) +{ + $PostFXManager::Settings::SSAO::blurDepthTol = $SSAOPostFx::blurDepthTol; + $PostFXManager::Settings::SSAO::blurNormalTol = $SSAOPostFx::blurNormalTol; + $PostFXManager::Settings::SSAO::lDepthMax = $SSAOPostFx::lDepthMax; + $PostFXManager::Settings::SSAO::lDepthMin = $SSAOPostFx::lDepthMin; + $PostFXManager::Settings::SSAO::lDepthPow = $SSAOPostFx::lDepthPow; + $PostFXManager::Settings::SSAO::lNormalPow = $SSAOPostFx::lNormalPow; + $PostFXManager::Settings::SSAO::lNormalTol = $SSAOPostFx::lNormalTol; + $PostFXManager::Settings::SSAO::lRadius = $SSAOPostFx::lRadius; + $PostFXManager::Settings::SSAO::lStrength = $SSAOPostFx::lStrength; + $PostFXManager::Settings::SSAO::overallStrength = $SSAOPostFx::overallStrength; + $PostFXManager::Settings::SSAO::quality = $SSAOPostFx::quality; + $PostFXManager::Settings::SSAO::sDepthMax = $SSAOPostFx::sDepthMax; + $PostFXManager::Settings::SSAO::sDepthMin = $SSAOPostFx::sDepthMin; + $PostFXManager::Settings::SSAO::sDepthPow = $SSAOPostFx::sDepthPow; + $PostFXManager::Settings::SSAO::sNormalPow = $SSAOPostFx::sNormalPow; + $PostFXManager::Settings::SSAO::sNormalTol = $SSAOPostFx::sNormalTol; + $PostFXManager::Settings::SSAO::sRadius = $SSAOPostFx::sRadius; + $PostFXManager::Settings::SSAO::sStrength = $SSAOPostFx::sStrength; + + postVerbose("% - PostFX Manager - Settings Saved - SSAO"); + +} + +function PostFXManager::settingsApplyHDR(%this) +{ + $PostFXManager::Settings::HDR::adaptRate = $HDRPostFX::adaptRate; + $PostFXManager::Settings::HDR::blueShiftColor = $HDRPostFX::blueShiftColor; + $PostFXManager::Settings::HDR::brightPassThreshold = $HDRPostFX::brightPassThreshold; + $PostFXManager::Settings::HDR::enableBloom = $HDRPostFX::enableBloom; + $PostFXManager::Settings::HDR::enableBlueShift = $HDRPostFX::enableBlueShift; + $PostFXManager::Settings::HDR::enableToneMapping = $HDRPostFX::enableToneMapping; + $PostFXManager::Settings::HDR::gaussMean = $HDRPostFX::gaussMean; + $PostFXManager::Settings::HDR::gaussMultiplier = $HDRPostFX::gaussMultiplier; + $PostFXManager::Settings::HDR::gaussStdDev = $HDRPostFX::gaussStdDev; + $PostFXManager::Settings::HDR::keyValue = $HDRPostFX::keyValue; + $PostFXManager::Settings::HDR::minLuminace = $HDRPostFX::minLuminace; + $PostFXManager::Settings::HDR::whiteCutoff = $HDRPostFX::whiteCutoff; + $PostFXManager::Settings::ColorCorrectionRamp = $HDRPostFX::colorCorrectionRamp; + + postVerbose("% - PostFX Manager - Settings Saved - HDR"); +} + +function PostFXManager::settingsApplyLightRays(%this) +{ + $PostFXManager::Settings::LightRays::brightScalar = $LightRayPostFX::brightScalar; + + $PostFXManager::Settings::LightRays::numSamples = $LightRayPostFX::numSamples; + $PostFXManager::Settings::LightRays::density = $LightRayPostFX::density; + $PostFXManager::Settings::LightRays::weight = $LightRayPostFX::weight; + $PostFXManager::Settings::LightRays::decay = $LightRayPostFX::decay; + + postVerbose("% - PostFX Manager - Settings Saved - Light Rays"); + +} + +function PostFXManager::settingsApplyDOF(%this) +{ + $PostFXManager::Settings::DOF::EnableAutoFocus = $DOFPostFx::EnableAutoFocus; + $PostFXManager::Settings::DOF::BlurMin = $DOFPostFx::BlurMin; + $PostFXManager::Settings::DOF::BlurMax = $DOFPostFx::BlurMax; + $PostFXManager::Settings::DOF::FocusRangeMin = $DOFPostFx::FocusRangeMin; + $PostFXManager::Settings::DOF::FocusRangeMax = $DOFPostFx::FocusRangeMax; + $PostFXManager::Settings::DOF::BlurCurveNear = $DOFPostFx::BlurCurveNear; + $PostFXManager::Settings::DOF::BlurCurveFar = $DOFPostFx::BlurCurveFar; + + postVerbose("% - PostFX Manager - Settings Saved - DOF"); + +} + +function PostFXManager::settingsApplyVignette(%this) +{ + $PostFXManager::Settings::Vignette::VMax = $VignettePostEffect::VMax; + $PostFXManager::Settings::Vignette::VMin = $VignettePostEffect::VMin; + + postVerbose("% - PostFX Manager - Settings Saved - Vignette"); + +} + +function PostFXManager::settingsApplyAll(%this, %sFrom) +{ + // Apply settings which control if effects are on/off altogether. + $PostFXManager::Settings::EnablePostFX = $PostFXManager::PostFX::Enabled; + $PostFXManager::Settings::EnableDOF = $PostFXManager::PostFX::EnableDOF; + $PostFXManager::Settings::EnableVignette = $PostFXManager::PostFX::EnableVignette; + $PostFXManager::Settings::EnableLightRays = $PostFXManager::PostFX::EnableLightRays; + $PostFXManager::Settings::EnableHDR = $PostFXManager::PostFX::EnableHDR; + $PostFXManager::Settings::EnableSSAO = $PostFXManager::PostFX::EnableSSAO; + + // Apply settings should save the values in the system to the + // the preset structure ($PostFXManager::Settings::*) + + // SSAO Settings + %this.settingsApplySSAO(); + // HDR settings + %this.settingsApplyHDR(); + // Light rays settings + %this.settingsApplyLightRays(); + // DOF + %this.settingsApplyDOF(); + // Vignette + %this.settingsApplyVignette(); + + postVerbose("% - PostFX Manager - All Settings applied to $PostFXManager::Settings"); +} + +function PostFXManager::settingsApplyDefaultPreset(%this) +{ + PostFXManager::loadPresetHandler($PostFXManager::defaultPreset); +} + diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.persistance.cs b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.persistance.cs new file mode 100644 index 000000000..31fec95f1 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/postFxManager.persistance.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// Used to name the saved files. +$PostFXManager::fileExtension = ".postfxpreset.cs"; + +// The filter string for file open/save dialogs. +$PostFXManager::fileFilter = "Post Effect Presets|*.postfxpreset.cs"; + +// Enable / disable PostFX when loading presets or just apply the settings? +$PostFXManager::forceEnableFromPresets = true; + +//Load a preset file from the disk, and apply the settings to the +//controls. If bApplySettings is true - the actual values in the engine +//will be changed to reflect the settings from the file. +function PostFXManager::loadPresetFile() +{ + //Show the dialog and set the flag + getLoadFilename($PostFXManager::fileFilter, "PostFXManager::loadPresetHandler"); +} + +function PostFXManager::loadPresetHandler( %filename ) +{ + //Check the validity of the file + if ( isScriptFile( %filename ) ) + { + %filename = expandFilename(%filename); + postVerbose("% - PostFX Manager - Executing " @ %filename); + exec(%filename); + + PostFXManager.settingsApplyFromPreset(); + } +} + +//Save a preset file to the specified file. The extension used +//is specified by $PostFXManager::fileExtension for on the fly +//name changes to the extension used. + +function PostFXManager::savePresetFile(%this) +{ + %defaultFile = filePath($Client::MissionFile) @ "/" @ fileBase($Client::MissionFile); + getSaveFilename($PostFXManager::fileFilter, "PostFXManager::savePresetHandler", %defaultFile); +} + +//Called from the PostFXManager::savePresetFile() function +function PostFXManager::savePresetHandler( %filename ) +{ + %filename = makeRelativePath( %filename, getMainDotCsDir() ); + if(strStr(%filename, ".") == -1) + %filename = %filename @ $PostFXManager::fileExtension; + + //Apply the current settings to the preset + PostFXManager.settingsApplyAll(); + + export("$PostFXManager::Settings::*", %filename, false); + + postVerbose("% - PostFX Manager - Save complete. Preset saved at : " @ %filename); +} + diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/ssao.cs b/Templates/BaseGame/game/data/postFX/scripts/client/ssao.cs new file mode 100644 index 000000000..729e0beea --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/ssao.cs @@ -0,0 +1,302 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +/// +$SSAOPostFx::overallStrength = 2.0; + +// TODO: Add small/large param docs. + +// The small radius SSAO settings. +$SSAOPostFx::sRadius = 0.1; +$SSAOPostFx::sStrength = 6.0; +$SSAOPostFx::sDepthMin = 0.1; +$SSAOPostFx::sDepthMax = 1.0; +$SSAOPostFx::sDepthPow = 1.0; +$SSAOPostFx::sNormalTol = 0.0; +$SSAOPostFx::sNormalPow = 1.0; + +// The large radius SSAO settings. +$SSAOPostFx::lRadius = 1.0; +$SSAOPostFx::lStrength = 10.0; +$SSAOPostFx::lDepthMin = 0.2; +$SSAOPostFx::lDepthMax = 2.0; +$SSAOPostFx::lDepthPow = 0.2; +$SSAOPostFx::lNormalTol = -0.5; +$SSAOPostFx::lNormalPow = 2.0; + +/// Valid values: 0, 1, 2 +$SSAOPostFx::quality = 0; + +/// +$SSAOPostFx::blurDepthTol = 0.001; + +/// +$SSAOPostFx::blurNormalTol = 0.95; + +/// +$SSAOPostFx::targetScale = "0.5 0.5"; + + +function SSAOPostFx::onAdd( %this ) +{ + %this.wasVis = "Uninitialized"; + %this.quality = "Uninitialized"; +} + +function SSAOPostFx::preProcess( %this ) +{ + if ( $SSAOPostFx::quality !$= %this.quality ) + { + %this.quality = mClamp( mRound( $SSAOPostFx::quality ), 0, 2 ); + + %this.setShaderMacro( "QUALITY", %this.quality ); + } + + %this.targetScale = $SSAOPostFx::targetScale; +} + +function SSAOPostFx::setShaderConsts( %this ) +{ + %this.setShaderConst( "$overallStrength", $SSAOPostFx::overallStrength ); + + // Abbreviate is s-small l-large. + + %this.setShaderConst( "$sRadius", $SSAOPostFx::sRadius ); + %this.setShaderConst( "$sStrength", $SSAOPostFx::sStrength ); + %this.setShaderConst( "$sDepthMin", $SSAOPostFx::sDepthMin ); + %this.setShaderConst( "$sDepthMax", $SSAOPostFx::sDepthMax ); + %this.setShaderConst( "$sDepthPow", $SSAOPostFx::sDepthPow ); + %this.setShaderConst( "$sNormalTol", $SSAOPostFx::sNormalTol ); + %this.setShaderConst( "$sNormalPow", $SSAOPostFx::sNormalPow ); + + %this.setShaderConst( "$lRadius", $SSAOPostFx::lRadius ); + %this.setShaderConst( "$lStrength", $SSAOPostFx::lStrength ); + %this.setShaderConst( "$lDepthMin", $SSAOPostFx::lDepthMin ); + %this.setShaderConst( "$lDepthMax", $SSAOPostFx::lDepthMax ); + %this.setShaderConst( "$lDepthPow", $SSAOPostFx::lDepthPow ); + %this.setShaderConst( "$lNormalTol", $SSAOPostFx::lNormalTol ); + %this.setShaderConst( "$lNormalPow", $SSAOPostFx::lNormalPow ); + + %blur = %this->blurY; + %blur.setShaderConst( "$blurDepthTol", $SSAOPostFx::blurDepthTol ); + %blur.setShaderConst( "$blurNormalTol", $SSAOPostFx::blurNormalTol ); + + %blur = %this->blurX; + %blur.setShaderConst( "$blurDepthTol", $SSAOPostFx::blurDepthTol ); + %blur.setShaderConst( "$blurNormalTol", $SSAOPostFx::blurNormalTol ); + + %blur = %this->blurY2; + %blur.setShaderConst( "$blurDepthTol", $SSAOPostFx::blurDepthTol ); + %blur.setShaderConst( "$blurNormalTol", $SSAOPostFx::blurNormalTol ); + + %blur = %this->blurX2; + %blur.setShaderConst( "$blurDepthTol", $SSAOPostFx::blurDepthTol ); + %blur.setShaderConst( "$blurNormalTol", $SSAOPostFx::blurNormalTol ); +} + +function SSAOPostFx::onEnabled( %this ) +{ + // This tells the AL shaders to reload and sample + // from our #ssaoMask texture target. + $AL::UseSSAOMask = true; + + return true; +} + +function SSAOPostFx::onDisabled( %this ) +{ + $AL::UseSSAOMask = false; +} + + +//----------------------------------------------------------------------------- +// GFXStateBlockData / ShaderData +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( SSAOStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampPoint; + samplerStates[1] = SamplerWrapLinear; + samplerStates[2] = SamplerClampPoint; +}; + +singleton GFXStateBlockData( SSAOBlurStateBlock : PFX_DefaultStateBlock ) +{ + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; + samplerStates[1] = SamplerClampPoint; +}; + +singleton ShaderData( SSAOShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/SSAO_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/gl/SSAO_P.glsl"; + + samplerNames[0] = "$prepassMap"; + samplerNames[1] = "$randNormalTex"; + samplerNames[2] = "$powTable"; + + pixVersion = 3.0; +}; + +singleton ShaderData( SSAOBlurYShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/SSAO_Blur_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/SSAO_Blur_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/gl/SSAO_Blur_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/gl/SSAO_Blur_P.glsl"; + + samplerNames[0] = "$occludeMap"; + samplerNames[1] = "$prepassMap"; + + pixVersion = 3.0; + + defines = "BLUR_DIR=float2(0.0,1.0)"; +}; + +singleton ShaderData( SSAOBlurXShader : SSAOBlurYShader ) +{ + defines = "BLUR_DIR=float2(1.0,0.0)"; +}; + +//----------------------------------------------------------------------------- +// PostEffects +//----------------------------------------------------------------------------- + +singleton PostEffect( SSAOPostFx ) +{ + allowReflectPass = false; + + renderTime = "PFXBeforeBin"; + renderBin = "AL_LightBinMgr"; + renderPriority = 10; + + shader = SSAOShader; + stateBlock = SSAOStateBlock; + + texture[0] = "#prepass"; + texture[1] = "data/postFX/art/noise.png"; + texture[2] = "#ssao_pow_table"; + + target = "$outTex"; + targetScale = "0.5 0.5"; + targetViewport = "PFXTargetViewport_NamedInTexture0"; + + singleton PostEffect() + { + internalName = "blurY"; + + shader = SSAOBlurYShader; + stateBlock = SSAOBlurStateBlock; + + texture[0] = "$inTex"; + texture[1] = "#prepass"; + + target = "$outTex"; + }; + + singleton PostEffect() + { + internalName = "blurX"; + + shader = SSAOBlurXShader; + stateBlock = SSAOBlurStateBlock; + + texture[0] = "$inTex"; + texture[1] = "#prepass"; + + target = "$outTex"; + }; + + singleton PostEffect() + { + internalName = "blurY2"; + + shader = SSAOBlurYShader; + stateBlock = SSAOBlurStateBlock; + + texture[0] = "$inTex"; + texture[1] = "#prepass"; + + target = "$outTex"; + }; + + singleton PostEffect() + { + internalName = "blurX2"; + + shader = SSAOBlurXShader; + stateBlock = SSAOBlurStateBlock; + + texture[0] = "$inTex"; + texture[1] = "#prepass"; + + // We write to a mask texture which is then + // read by the lighting shaders to mask ambient. + target = "#ssaoMask"; + }; +}; + + +/// Just here for debug visualization of the +/// SSAO mask texture used during lighting. +singleton PostEffect( SSAOVizPostFx ) +{ + allowReflectPass = false; + + shader = PFX_PassthruShader; + stateBlock = PFX_DefaultStateBlock; + + texture[0] = "#ssaoMask"; + + target = "$backbuffer"; +}; + +singleton ShaderData( SSAOPowTableShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/SSAO_PowerTable_V.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/SSAO_PowerTable_P.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/gl/SSAO_PowerTable_V.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/ssao/gl/SSAO_PowerTable_P.glsl"; + + pixVersion = 2.0; +}; + +singleton PostEffect( SSAOPowTablePostFx ) +{ + shader = SSAOPowTableShader; + stateBlock = PFX_DefaultStateBlock; + + renderTime = "PFXTexGenOnDemand"; + + target = "#ssao_pow_table"; + + targetFormat = "GFXFormatR16F"; + targetSize = "256 1"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/turbulence.cs b/Templates/BaseGame/game/data/postFX/scripts/client/turbulence.cs new file mode 100644 index 000000000..7842a6429 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/turbulence.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GFXStateBlockData( PFX_TurbulenceStateBlock : PFX_DefaultStateBlock) +{ + zDefined = false; + zEnable = false; + zWriteEnable = false; + + samplersDefined = true; + samplerStates[0] = SamplerClampLinear; +}; + +singleton ShaderData( PFX_TurbulenceShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/turbulenceP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/gl/turbulenceP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; +}; + +singleton PostEffect( TurbulenceFx ) +{ + isEnabled = false; + allowReflectPass = true; + + renderTime = "PFXAfterDiffuse"; + renderBin = "GlowBin"; + renderPriority = 0.5; // Render after the glows themselves + + shader = PFX_TurbulenceShader; + stateBlock=PFX_TurbulenceStateBlock; + texture[0] = "$backBuffer"; + }; diff --git a/Templates/BaseGame/game/data/postFX/scripts/client/vignette.cs b/Templates/BaseGame/game/data/postFX/scripts/client/vignette.cs new file mode 100644 index 000000000..c824f5240 --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/client/vignette.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$VignettePostEffect::VMax = 0.6; +$VignettePostEffect::VMin = 0.2; + +singleton ShaderData( VignetteShader ) +{ + DXVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.hlsl"; + DXPixelShaderFile = $Core::CommonShaderPath @ "/postFx/vignette/VignetteP.hlsl"; + + OGLVertexShaderFile = $Core::CommonShaderPath @ "/postFx/postFxV.glsl"; + OGLPixelShaderFile = $Core::CommonShaderPath @ "/postFx/vignette/gl/VignetteP.glsl"; + + samplerNames[0] = "$backBuffer"; + + pixVersion = 2.0; +}; + +singleton PostEffect( VignettePostEffect ) +{ + isEnabled = false; + allowReflectPass = false; + renderTime = "PFXAfterBin"; + renderBin = "GlowBin"; + shader = VignetteShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$backBuffer"; + renderPriority = 10; +}; + +function VignettePostEffect::setShaderConsts(%this) +{ + %this.setShaderConst("$Vmax", $VignettePostEffect::VMax); + %this.setShaderConst("$Vmin", $VignettePostEffect::VMin); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/postFX/scripts/gui/postFxManager.gui b/Templates/BaseGame/game/data/postFX/scripts/gui/postFxManager.gui new file mode 100644 index 000000000..78248e2ae --- /dev/null +++ b/Templates/BaseGame/game/data/postFX/scripts/gui/postFxManager.gui @@ -0,0 +1,2755 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(PostFXManager) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiModelessDialogProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new DbgFileView() { + position = "0 0"; + extent = "8 2"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiWindowCtrl(ppOptionsWindow) { + text = "PostFX Manager"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "Canvas.popDialog(PostFXManager);"; + edgeSnap = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "306 216"; + extent = "411 336"; + minExtent = "8 8"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + position = "11 77"; + extent = "390 216"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTabBookCtrl(ppOptionsTabBook) { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "32"; + tabHeight = "20"; + allowReorder = "0"; + defaultPage = "-1"; + selectedPage = "1"; + frontTabPadding = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 58"; + extent = "394 233"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl(ppOptionsSSAOTab) { + fitBook = "0"; + text = "SSAO"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "394 213"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Screen Space Ambient Occlusion postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + position = "12 30"; + extent = "365 170"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTabBookCtrl(ppOptionsSSAOOptions) { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "64"; + tabHeight = "20"; + allowReorder = "0"; + defaultPage = "-1"; + selectedPage = "2"; + frontTabPadding = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 11"; + extent = "362 185"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl(ppOptionsSSAOGeneralTab) { + fitBook = "0"; + text = "General"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "362 165"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains general overall settings for the SSAO postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(ppOptionsSSAOOverallStrengthLabel) { + text = "Overall Strength"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "31 57"; + extent = "77 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the overall strength of the Ambient Occlusion effect."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOBlurDepthLabel) { + text = "Blur (Softness)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "38 85"; + extent = "73 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the amount of softness in the SSAO, overall."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOBlurNormalLabel) { + text = "Blur (Normal Maps)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "19 112"; + extent = "92 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the amount of softness in the SSAO, in the normal maps."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(ppOptionsSSAOQuality) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Low"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "120 28"; + extent = "211 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOQualityLabel) { + text = "Quality"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "76 29"; + extent = "32 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOOverallStrength) { + range = "0 50"; + ticks = "1000"; + snap = "0"; + value = "2"; + position = "120 56"; + extent = "211 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOBlurDepth) { + range = "0 0.3"; + ticks = "1000"; + snap = "0"; + value = "0.001"; + position = "120 86"; + extent = "211 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOBlurNormal) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0.95"; + position = "119 113"; + extent = "212 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsSSAONearTab) { + fitBook = "0"; + text = "Near"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "362 165"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings for the near range ambient occlusion aspect of the SSAO postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl(ppOptionsSSAONearRadius) { + range = "0.001 5"; + ticks = "1000"; + snap = "0"; + value = "0.1"; + position = "122 17"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAONearDepthMin) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "0.1"; + position = "122 62"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAONearStrength) { + range = "0 20"; + ticks = "1000"; + snap = "0"; + value = "6"; + position = "122 39"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearRadiusLabel) { + text = "Radius"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 16"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the near/small radius SSAO reach."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearStrengthLabel) { + text = "Strength"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "73 38"; + extent = "41 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the near/small radius SSAO strength."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearDepthMinLabel) { + text = "Depth Min"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "66 61"; + extent = "48 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the near/small radius SSAO minimum depth value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearDepthMaxLabel) { + text = "Depth Max"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "62 85"; + extent = "52 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the near/small radius SSAO maximum depth value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAONearDepthMax) { + range = "0 50"; + ticks = "1000"; + snap = "0"; + value = "1"; + position = "122 86"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAONearToleranceNormal) { + range = "0 2"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "122 133"; + extent = "103 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAONearTolerancePower) { + range = "0 2"; + ticks = "1000"; + snap = "0"; + value = "1"; + position = "246 133"; + extent = "97 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearToleranceLabel2) { + text = "Tolerance / Power"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "24 132"; + extent = "92 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAONearToleranceLabel1) { + text = "Normal Maps : "; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "19 113"; + extent = "71 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsSSAOFarTab) { + fitBook = "0"; + text = "Far"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "362 165"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings for the far range ambient occlusion aspect of the SSAO postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(ppOptionsSSAOFarRadiusLabel) { + text = "Radius"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 16"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the far/large radius SSAO reach."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarRadius) { + range = "0.001 5"; + ticks = "1000"; + snap = "0"; + value = "1"; + position = "122 17"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOFarStrengthLabel) { + text = "Strength"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "73 38"; + extent = "41 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the far/large radius SSAO strength."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarStrength) { + range = "0 20"; + ticks = "1000"; + snap = "0"; + value = "10"; + position = "122 39"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOFarDepthMinLabel) { + text = "Depth Min"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "66 61"; + extent = "48 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the far/large radius SSAO minimum depth."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarDepthMin) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "0.2"; + position = "122 62"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOFarDepthMaxLabel) { + text = "Depth Max"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "62 85"; + extent = "52 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the far/large radius SSAO maximum."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarDepthMax) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "2"; + position = "122 86"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsSSAOFarToleranceLabel1) { + text = "Normal Maps :"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 113"; + extent = "72 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Tolerance / Power"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "24 132"; + extent = "90 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarToleranceNormal) { + range = "0 2"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "122 133"; + extent = "100 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsSSAOFarTolerancePower) { + range = "0 2"; + ticks = "1000"; + snap = "0"; + value = "2"; + position = "239 133"; + extent = "104 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiCheckBoxCtrl(ppOptionsEnableSSAO) { + useInactiveState = "0"; + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the SSAO postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsHDRTab) { + fitBook = "0"; + text = "HDR"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "394 213"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the High Definition Range Lighting postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + position = "12 30"; + extent = "363 172"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTabBookCtrl(ppOptionsHDROptions) { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "64"; + tabHeight = "20"; + allowReorder = "0"; + defaultPage = "-1"; + selectedPage = "0"; + frontTabPadding = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 11"; + extent = "365 195"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl(ppOptionsHDRBrightnessTab) { + fitBook = "0"; + text = "Brightness"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "365 175"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings related to the brightness of the HDR postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl(ppOptionsHDRMinLuminance) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "132 77"; + extent = "206 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 0"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRKeyValue) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0.0459184"; + position = "132 50"; + extent = "206 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 0.0459184"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRKeyValueLabel) { + text = "Key Value"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "69 50"; + extent = "52 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The tone mapping middle grey or exposure value used to adjust the overall \"balance\" of the image."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRMinLuminanceLabel) { + text = "Minimum Luminance"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "25 77"; + extent = "96 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The minimum luminance value to allow when tone mapping the scene. This is particularly useful if your scene is very dark or has a black ambient color in places."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRWhiteCutoffLabel) { + text = "White Cutoff"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "56 104"; + extent = "65 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The cutoff level for the white levels in the brightness."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRWhiteCutoff) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0.52551"; + position = "132 104"; + extent = "206 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 0.52551"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRBrightnessAdaptRate) { + range = "0.1 10"; + ticks = "1000"; + snap = "0"; + value = "2"; + position = "132 132"; + extent = "205 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRBrightnessAdaptRateLabel) { + text = "Brightness Adapt Rate"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 132"; + extent = "109 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The speed at which the view adjusts to the new lighting in the environment."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRKeyValueLabel1) { + text = "Tone Mapping Contrast"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 24"; + extent = "111 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Tone mapping contrast is the amount of scene to blend, with the tone mapped HDR scene. Lower values are recommended but higher values give a strong contrasted darker shadowed look."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRToneMappingAmount) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0.265306"; + position = "132 24"; + extent = "206 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "value : 0.265306"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsHDRBloomTab) { + fitBook = "0"; + text = "Bloom"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "365 175"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings related to the blooming aspect of the HDR postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl(ppOptionsHDRBloomBlurMultiplier) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "0.502645"; + position = "132 70"; + extent = "199 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 0.502645"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRBloomBlurMean) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0.510526"; + position = "132 97"; + extent = "200 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 0.510526"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRBloomBlurStdDev) { + range = "0 3"; + ticks = "1000"; + snap = "0"; + value = "1.4127"; + position = "132 123"; + extent = "199 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 1.4127"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRBlurMultiplierLabel) { + text = "Blur Multiplier"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "59 70"; + extent = "63 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The amount of blur to apply to the bloomed areas in the HDR."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRBlurMeanLabel) { + text = "Blur \"mean\" value"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "38 97"; + extent = "84 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRBlurStandardDevianceLabel) { + text = "Blur \"Std Dev\" value"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "23 123"; + extent = "99 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsHDRBloomBrightPassThresholdLabel) { + text = "Bright pass threshold"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "19 43"; + extent = "103 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The bright pass threshold controls how bright the brightest areas of the scene are in the HDR."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsHDRBloomBlurBrightPassThreshold) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "1.60526"; + position = "132 43"; + extent = "200 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Value : 1.60526"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppOptionsHDRBloom) { + useInactiveState = "0"; + text = " Enable Bloom"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 9"; + extent = "85 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enables or disables the bloom (glowing effect) of the HDR PostFX."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsHDRBloomEffectsTab) { + fitBook = "0"; + text = "Effects"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "365 175"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings related to the effects the HDR postFX can offer"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiCheckBoxCtrl(ppOptionsHDREffectsBlueShift) { + useInactiveState = "0"; + text = " Enable Color Shift"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "11 4"; + extent = "117 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enables a scene tinting/Blue shift based on the color selected below."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiColorPickerCtrl(ppOptionsHDREffectsBlueShiftColorBlend) { + baseColor = "1 0 0.0235294 1"; + pickColor = "0 0 0 1"; + selectorGap = "1"; + displayMode = "BlendColor"; + actionOnMove = "1"; + showReticle = "1"; + position = "10 29"; + extent = "344 110"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Select a color"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiColorPickerCtrl(ppOptionsHDREffectsBlueShiftColorBaseColor) { + baseColor = "1 0 0.0235294 1"; + pickColor = "0 0 0 1"; + selectorGap = "1"; + displayMode = "HorizColor"; + actionOnMove = "1"; + showReticle = "1"; + position = "10 142"; + extent = "343 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Select a color"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiCheckBoxCtrl(ppOptionsHDRToneMapping) { + useInactiveState = "0"; + text = " Enable Tone Mapping"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "18 8"; + extent = "120 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enables or disabled tone mapping on the HDR. The tone mapping balanced the brightness levels during the HDR process. Recommended"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppOptionsEnableHDR) { + useInactiveState = "0"; + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the HDR postFX (takes some time to initialise, be patient)"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppOptionsEnableHDRDebug) { + useInactiveState = "0"; + text = "Debug"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "262 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsLightRaysTab) { + fitBook = "0"; + text = "Light Rays"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "394 213"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Light Rays postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl(ppOptionsLightRaysBrightScalar) { + range = "0 5"; + ticks = "1000"; + snap = "0"; + value = "0.75"; + position = "96 46"; + extent = "264 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsLightRaysBrightnessScalarLabel) { + text = "Brightness"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 48"; + extent = "87 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls how bright the rays and the object casting them are in the scene."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiSliderCtrl(ppOptionsLightRaysSampleScalar) { + range = "20 512"; + ticks = "512"; + snap = "0"; + value = "40"; + position = "96 75"; + extent = "264 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiTextCtrl(ppOptionsLightRaysSampleScalarLabel) { + text = "Samples"; + maxLength = "512"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 76"; + extent = "87 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the number of samples for the shader."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiSliderCtrl(ppOptionsLightRaysDensityScalar) { + range = "0.01 1"; + ticks = "1000"; + snap = "0"; + value = "0.94"; + position = "96 105"; + extent = "264 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiTextCtrl(ppOptionsLightRaysDensityScalarLabel) { + text = "Density"; + maxLength = "1000"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 106"; + extent = "87 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the density of the rays."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiSliderCtrl(ppOptionsLightRaysWeightScalar) { + range = "0.1 10"; + ticks = "1000"; + snap = "0"; + value = "5.65"; + position = "96 135"; + extent = "264 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiTextCtrl(ppOptionsLightRaysWeightScalarLabel) { + text = "Weight"; + maxLength = "1000"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 136"; + extent = "87 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the weight of the rays."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + + new GuiSliderCtrl(ppOptionsLightRaysDecayScalar) { + range = "0.01 1"; + ticks = "1000"; + snap = "0"; + value = "1.0"; + position = "96 165"; + extent = "264 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiTextCtrl(ppOptionsLightRaysDecayScalarLabel) { + text = "Decay"; + maxLength = "1000"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 166"; + extent = "87 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the decay of the rays."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppOptionsEnableLightRays) { + useInactiveState = "0"; + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the light rays postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsDOFTab) { + fitBook = "0"; + text = "DOF"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "394 213"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Depth Of Field postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + position = "14 28"; + extent = "362 170"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTabBookCtrl() { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "64"; + tabHeight = "20"; + allowReorder = "0"; + defaultPage = "-1"; + selectedPage = "1"; + frontTabPadding = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "14 9"; + extent = "360 189"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl(ppOptionsDOFGeneralTab) { + fitBook = "0"; + text = "General"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "360 169"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains general settings related to the DOF system"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + //new GuiCheckBoxCtrl(ppOptionsDOFEnableDOF) { + //useInactiveState = "0"; + //text = "Enable DOF"; + //groupNum = "-1"; + //buttonType = "ToggleButton"; + //useMouseEvents = "0"; + //position = "31 43"; + //extent = "140 30"; + //minExtent = "8 2"; + //horizSizing = "right"; + //vertSizing = "bottom"; + //profile = "GuiCheckBoxProfile"; + //visible = "1"; + //active = "1"; + //tooltipProfile = "GuiToolTipProfile"; + //hovertime = "1000"; + //isContainer = "0"; + //canSave = "1"; + //canSaveDynamicFields = "0"; + //}; + new GuiCheckBoxCtrl(ppOptionsDOFEnableAutoFocus) { + useInactiveState = "0"; + text = "Enable Auto Focus"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "31 8"; + extent = "140 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsDOFAutoFocusTab) { + fitBook = "0"; + text = "Auto Focus"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 20"; + extent = "360 169"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Contains settings related to the fine control of the auto focus system"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(ppOptionsDOFNearBlurMaxLabel) { + text = "Near Blur Max"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "36 8"; + extent = "67 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The max allowed value of near blur"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFFarBlurMinSlider) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "120 8"; + extent = "224 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsDOFFarBlurMaxLabel) { + text = "Far Blur Max"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "43 34"; + extent = "60 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The max allowed value of far blur"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFFarBlurMaxSlider) { + range = "0 1"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "120 34"; + extent = "224 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsDOFFocusRangeMinLabel) { + text = "Focus Range (Min)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "13 61"; + extent = "90 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The distance range around the focal distance that remains in focus (in meters, minimum distance in focus) focal distance it is\r\ndependant on the visible distance set in your level"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFFocusRangeMinSlider) { + range = "0.01 1e+003"; + ticks = "1000"; + snap = "0"; + value = "0.01"; + position = "120 61"; + extent = "224 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsDOFFocusRangeMaxLabel) { + text = "Focus Range (Max)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "9 88"; + extent = "95 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "The distance range around the focal distance that remains in focus (in meters, maximum distance in focus) focal distance it is\r\ndependant on the visible distance set in your level"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFFocusRangeMaxSlider) { + range = "0.01 1e+003"; + ticks = "1000"; + snap = "0"; + value = "0.01"; + position = "119 87"; + extent = "224 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsDOFBurCurveNearLabel) { + text = "Blur Curve Near"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "27 114"; + extent = "77 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "A small number causes bluriness to increase gradually\r\nat distances closer than the focal distance. A large number causes bluriness to \r\nincrease quickly"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFBlurCurveNearSlider) { + range = "0 50"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "119 114"; + extent = "225 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsDOFBlurCurveFarLabel) { + text = "Blur Curve Far"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "33 139"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "A small number causes bluriness to increase gradually\r\nat distances closer than the focal distance. A large number causes bluriness to \r\nincrease quickly"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsDOFBlurCurveFarSlider) { + range = "0 50"; + ticks = "1000"; + snap = "0"; + value = "0"; + position = "119 141"; + extent = "224 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiCheckBoxCtrl(ppOptionsEnableDOF) { + useInactiveState = "0"; + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the Depth of field postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl(ppOptionsVignetteTab) { + fitBook = "0"; + text = "Vignette"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 40"; + extent = "394 193"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Vignette postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + + new GuiCheckBoxCtrl(ppOptionsEnableVignette) { + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the vignette postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(ppOptionsVignetteVMax) { + range = "0.001 5"; + ticks = "1000"; + snap = "0"; + value = "0.6"; + position = "96 46"; + extent = "221 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderBoxProfile"; + visible = "1"; + active = "1"; + variable = "$VignettePostEffect::VMax"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsVignetteVMaxLabel) { + text = "Radius"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "26 48"; + extent = "41 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Controls the maximum exposure of vignetting."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTabPageCtrl() { + fitBook = "0"; + text = "Color Correction"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "8 27"; + extent = "376 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "ColorCorrectionTab"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Color Correction Ramp"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 7"; + extent = "118 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "data/postFX/art/null_color_ramp.png"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 29"; + extent = "365 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + altCommand = "ppColorCorrection_selectFileHandler( $thisControl.getText() );"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "ColorCorrectionFileName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Select..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "252 54"; + extent = "56 22"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ppColorCorrection_selectFile();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorCorrectionButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Reset"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "315 54"; + extent = "56 22"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ppColorCorrection_selectFileHandler( \"\" );"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorCorrectionReset"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiButtonCtrl(ppOptionsApply) { + text = "Apply"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "309 302"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "PostFXManager.settingsApplyAll(); Canvas.popDialog(PostFXManager);"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Apply the settings and close this dialog"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsSavePreset) { + text = "Save Preset..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "111 302"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "PostFXManager.savePresetFile();"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Save the preset to a file to disk for later use (use postfx::applyPreset)"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsLoadPreset) { + text = "Load Preset..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "12 302"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "PostFXManager.loadPresetFile();"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Load a post FX preset file from disk"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppOptionsEnable) { + useInactiveState = "0"; + text = "Enable PostFX System"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 24"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the postFX system"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(ppOptionsQuality) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "278 30"; + extent = "122 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Used to adjust the quality/performance settings of the PostFX system. Some PostFX may not adhere to the settings in this dialog."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(ppOptionsQualityLabel) { + text = "Quality"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "238 32"; + extent = "39 12"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Used to adjust the quality/performance settings of the PostFX system. Some PostFX may not adhere to the settings in this dialog."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsOk1) { + text = "Revert"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "210 302"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "postProcessOptionsDlg.applySettings(); Canvas.popDialog(postProcessOptionsDlg);"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Revert any changes made since opening the dialog"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogP.hlsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogP.hlsl new file mode 100644 index 000000000..e900f7548 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogP.hlsl @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog final pixel shader V2.00 +#include "../shaderModel.hlsl" +#include "../shaderModelAutoGen.hlsl" +#include "../torque.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); +TORQUE_UNIFORM_SAMPLER2D(depthBuffer, 1); +TORQUE_UNIFORM_SAMPLER2D(frontBuffer, 2); +TORQUE_UNIFORM_SAMPLER2D(density, 3); + +uniform float3 ambientColor; +uniform float accumTime; +uniform float4 fogColor; +uniform float4 modspeed;//xy speed layer 1, zw speed layer 2 +uniform float2 viewpoint; +uniform float2 texscale; +uniform float fogDensity; +uniform float preBias; +uniform float textured; +uniform float modstrength; +uniform float numtiles; +uniform float fadesize; +uniform float2 PixelSize; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 htpos : TEXCOORD0; + float2 uv0 : TEXCOORD1; +}; + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + float2 uvscreen=((IN.htpos.xy/IN.htpos.w) + 1.0 ) / 2.0; + uvscreen.y = 1.0 - uvscreen.y; + + float obj_test = TORQUE_PREPASS_UNCONDITION(prepassTex, uvscreen).w * preBias; + float depth = TORQUE_TEX2D(depthBuffer, uvscreen).r; + float front = TORQUE_TEX2D(frontBuffer, uvscreen).r; + + if (depth <= front) + return float4(0,0,0,0); + else if ( obj_test < depth ) + depth = obj_test; + if ( front >= 0.0) + depth -= front; + + float diff = 1.0; + float3 col = fogColor.rgb; + if (textured != 0.0) + { + float2 offset = viewpoint + ((-0.5 + (texscale * uvscreen)) * numtiles); + + float2 mod1 = TORQUE_TEX2D(density, (offset + (modspeed.xy*accumTime))).rg; + float2 mod2 = TORQUE_TEX2D(density, (offset + (modspeed.zw*accumTime))).rg; + diff = (mod2.r + mod1.r) * modstrength; + col *= (2.0 - ((mod1.g + mod2.g) * fadesize))/2.0; + } + + col *= ambientColor; + + float4 resultColor = float4(col, 1.0 - saturate(exp(-fogDensity * depth * diff * fadesize))); + + return hdrEncode(resultColor); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreP.hlsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreP.hlsl new file mode 100644 index 000000000..fdc839507 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreP.hlsl @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog prepass pixel shader V1.00 +#include "../shaderModel.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 pos : TEXCOORD0; +}; + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + float OUT; + + clip( IN.pos.w ); + OUT = IN.pos.w; + + return float4(OUT,0,0,1); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreV.hlsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreV.hlsl new file mode 100644 index 000000000..aba7a745d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogPreV.hlsl @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog prepass vertex shader V1.00 + +#include "../shaderModel.hlsl" +#include "../hlslStructs.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 pos : TEXCOORD0; +}; + +uniform float4x4 modelView; + +ConnectData main( VertexIn_P IN) +{ + ConnectData OUT; + + OUT.hpos = mul(modelView, float4(IN.pos, 1.0)); + OUT.pos = OUT.hpos; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogRefl.hlsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogRefl.hlsl new file mode 100644 index 000000000..380233b5f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogRefl.hlsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog Reflection pixel shader V1.00 +#include "../shaderModel.hlsl" +uniform float4 fogColor; +uniform float fogDensity; +uniform float reflStrength; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 pos : TEXCOORD0; +}; + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + return float4(fogColor.rgb,saturate(fogDensity*reflStrength)); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogV.hlsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogV.hlsl new file mode 100644 index 000000000..167f83946 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/VFogV.hlsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog final vertex shader V1.00 + +#include "../shaderModel.hlsl" +#include "../hlslStructs.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 htpos : TEXCOORD0; + float2 uv0 : TEXCOORD1; +}; + +uniform float4x4 modelView; + +ConnectData main( VertexIn_PNTT IN) +{ + ConnectData OUT; + + OUT.hpos = mul(modelView, float4(IN.pos,1.0)); + OUT.htpos = OUT.hpos; + OUT.uv0 = IN.uv0; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogP.glsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogP.glsl new file mode 100644 index 000000000..7895d9e2d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogP.glsl @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/torque.glsl" + +uniform sampler2D prepassTex; +uniform sampler2D depthBuffer; +uniform sampler2D frontBuffer; +uniform sampler2D density; + +uniform float accumTime; +uniform vec4 fogColor; +uniform float fogDensity; +uniform float preBias; +uniform float textured; +uniform float modstrength; +uniform vec4 modspeed;//xy speed layer 1, zw speed layer 2 +uniform vec2 viewpoint; +uniform vec2 texscale; +uniform vec3 ambientColor; +uniform float numtiles; +uniform float fadesize; +uniform vec2 PixelSize; + +in vec4 _hpos; +#define IN_hpos _hpos +out vec4 OUT_col; + +void main() +{ + vec2 uvscreen=((IN_hpos.xy/IN_hpos.w) + 1.0 ) / 2.0; + uvscreen.y = 1.0 - uvscreen.y; + + float obj_test = prepassUncondition( prepassTex, uvscreen).w * preBias; + float depth = tex2D(depthBuffer,uvscreen).r; + float front = tex2D(frontBuffer,uvscreen).r; + + if (depth <= front) + { + OUT_col = vec4(0,0,0,0); + return; + } + + else if ( obj_test < depth ) + depth = obj_test; + if ( front >= 0.0) + depth -= front; + + float diff = 1.0; + vec3 col = fogColor.rgb; + if (textured != 0.0) + { + vec2 offset = viewpoint + ((-0.5 + (texscale * uvscreen)) * numtiles); + + vec2 mod1 = tex2D(density,(offset + (modspeed.xy*accumTime))).rg; + vec2 mod2= tex2D(density,(offset + (modspeed.zw*accumTime))).rg; + diff = (mod2.r + mod1.r) * modstrength; + col *= (2.0 - ((mod1.g + mod2.g) * fadesize))/2.0; + } + + col *= ambientColor; + + vec4 returnColor = vec4(col, 1.0 - saturate(exp(-fogDensity * depth * diff * fadesize))); + + OUT_col = hdrEncode(returnColor); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreP.glsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreP.glsl new file mode 100644 index 000000000..017ea6ef8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreP.glsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +in vec4 _hpos; +#define IN_hpos _hpos + +out vec4 OUT_col; + +void main() +{ + float OUT; + clip( IN_hpos.w ); + OUT = IN_hpos.w; + + OUT_col = vec4(OUT,0,0,1); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreV.glsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreV.glsl new file mode 100644 index 000000000..2f2a1318a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogPreV.glsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +#define IN_position vPosition + +out vec4 _hpos; +#define OUT_hpos _hpos + +uniform mat4 modelView; + +void main() +{ + vec4 inPos = IN_position; + inPos.w = 1.0; + + OUT_hpos = tMul( modelView, inPos ); + + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogRefl.glsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogRefl.glsl new file mode 100644 index 000000000..78e149fbf --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogRefl.glsl @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +uniform vec4 fogColor; +uniform float fogDensity; +uniform float reflStrength; +out vec4 OUT_col; + +void main() +{ + OUT_col = vec4(fogColor.rgb,saturate(fogDensity*reflStrength)); +} diff --git a/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogV.glsl b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogV.glsl new file mode 100644 index 000000000..57b3ba87e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/VolumetricFog/gl/VFogV.glsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +#define IN_position vPosition + +out vec4 _hpos; +#define OUT_hpos _hpos + +uniform mat4 modelView; + +void main() +{ + OUT_hpos = tMul(modelView, IN_position); + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/basicCloudsP.hlsl b/Templates/BaseGame/game/data/shaders/common/basicCloudsP.hlsl new file mode 100644 index 000000000..4b40e5e8c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/basicCloudsP.hlsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + float4 col = TORQUE_TEX2D(diffuseMap, IN.texCoord); + return hdrEncode( col ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/basicCloudsV.hlsl b/Templates/BaseGame/game/data/shaders/common/basicCloudsV.hlsl new file mode 100644 index 000000000..a176fdbcd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/basicCloudsV.hlsl @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct CloudVert +{ + float3 pos : POSITION; + float2 uv0 : TEXCOORD0; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; +}; + +uniform float4x4 modelview; +uniform float2 texDirection; +uniform float2 texOffset; +uniform float accumTime; +uniform float texScale; + + +ConnectData main( CloudVert IN ) +{ + ConnectData OUT; + + OUT.hpos = mul(modelview, float4(IN.pos,1.0)); + + float2 uv = IN.uv0; + uv += texOffset; + uv *= texScale; + uv += accumTime * texDirection; + + OUT.texCoord = uv; + + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/cloudLayerP.hlsl b/Templates/BaseGame/game/data/shaders/common/cloudLayerP.hlsl new file mode 100644 index 000000000..efa8fe0b4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/cloudLayerP.hlsl @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" +#include "torque.hlsl" + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 texCoord12 : TEXCOORD0; + float4 texCoord34 : TEXCOORD1; + float3 vLightTS : TEXCOORD2; // light vector in tangent space, denormalized + float3 vViewTS : TEXCOORD3; // view vector in tangent space, denormalized + float worldDist : TEXCOORD4; +}; + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +TORQUE_UNIFORM_SAMPLER2D(normalHeightMap, 0); +uniform float3 ambientColor; +uniform float3 sunColor; +uniform float cloudCoverage; +uniform float3 cloudBaseColor; +uniform float cloudExposure; + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +// The per-color weighting to be used for luminance calculations in RGB order. +static const float3 LUMINANCE_VECTOR = float3(0.2125f, 0.7154f, 0.0721f); + + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +// Calculates the Rayleigh phase function +float getRayleighPhase( float angle ) +{ + return 0.75 * ( 1.0 + pow( angle, 2 ) ); +} + +// Returns the output rgb color given a texCoord and parameters it uses +// for lighting calculation. +float3 ComputeIllumination( float2 texCoord, + float3 vLightTS, + float3 vViewTS, + float3 vNormalTS ) +{ + //return noiseNormal; + //return vNormalTS; + + float3 vLightTSAdj = float3( -vLightTS.x, -vLightTS.y, vLightTS.z ); + + float dp = dot( vNormalTS, vLightTSAdj ); + + // Calculate the amount of illumination (lightTerm)... + + // We do both a rim lighting effect and a halfLambertian lighting effect + // and combine the result. + float halfLambertTerm = saturate( pow( dp * 0.5 + 0.5, 1 ) ); + float rimLightTerm = pow( ( 1.0 - dp ), 1.0 ); + float lightTerm = saturate( halfLambertTerm * 1.0 + rimLightTerm * dp ); + lightTerm *= 0.5; + + // Use a simple RayleighPhase function to simulate single scattering towards + // the camera. + float angle = dot( vLightTS, vViewTS ); + lightTerm *= getRayleighPhase( angle ); + + // Combine terms and colors into the output color. + //float3 lightColor = ( lightTerm * sunColor * fOcclusionShadow ) + ambientColor; + float3 lightColor = lerp( ambientColor, sunColor, lightTerm ); + //lightColor = lerp( lightColor, ambientColor, cloudCoverage ); + float3 finalColor = cloudBaseColor * lightColor; + + return finalColor; +} + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + // Normalize the interpolated vectors: + float3 vViewTS = normalize( IN.vViewTS ); + float3 vLightTS = normalize( IN.vLightTS ); + + float4 cResultColor = float4( 0, 0, 0, 1 ); + + float2 texSample = IN.texCoord12.xy; + + float4 noise1 = TORQUE_TEX2D( normalHeightMap, IN.texCoord12.zw ); + noise1 = normalize( ( noise1 - 0.5 ) * 2.0 ); + //return noise1; + + float4 noise2 = TORQUE_TEX2D(normalHeightMap, IN.texCoord34.xy); + noise2 = normalize( ( noise2 - 0.5 ) * 2.0 ); + //return noise2; + + float3 noiseNormal = normalize( noise1 + noise2 ).xyz; + //return float4( noiseNormal, 1.0 ); + + float noiseHeight = noise1.a * noise2.a * ( cloudCoverage / 2.0 + 0.5 ); + + float3 vNormalTS = normalize( TORQUE_TEX2D(normalHeightMap, texSample).xyz * 2.0 - 1.0); + vNormalTS += noiseNormal; + vNormalTS = normalize( vNormalTS ); + + // Compute resulting color for the pixel: + cResultColor.rgb = ComputeIllumination( texSample, vLightTS, vViewTS, vNormalTS ); + + float coverage = ( cloudCoverage - 0.5 ) * 2.0; + cResultColor.a = TORQUE_TEX2D(normalHeightMap, texSample).a + coverage + noiseHeight; + + if ( cloudCoverage > -1.0 ) + cResultColor.a /= 1.0 + coverage; + + cResultColor.a = saturate( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ) ); + + cResultColor.a = lerp( cResultColor.a, 0.0, 1.0 - pow(IN.worldDist,2.0) ); + + cResultColor.rgb *= cloudExposure; + + return hdrEncode( cResultColor ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/cloudLayerV.hlsl b/Templates/BaseGame/game/data/shaders/common/cloudLayerV.hlsl new file mode 100644 index 000000000..d60dd251d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/cloudLayerV.hlsl @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +#include "shaderModel.hlsl" + +struct CloudVert +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float3 binormal : BINORMAL; + float3 tangent : TANGENT; + float2 uv0 : TEXCOORD0; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 texCoord12 : TEXCOORD0; + float4 texCoord34 : TEXCOORD1; + float3 vLightTS : TEXCOORD2; // light vector in tangent space, denormalized + float3 vViewTS : TEXCOORD3; // view vector in tangent space, denormalized + float worldDist : TEXCOORD4; +}; + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform float4x4 modelview; +uniform float3 eyePosWorld; +uniform float3 sunVec; +uniform float2 texOffset0; +uniform float2 texOffset1; +uniform float2 texOffset2; +uniform float3 texScale; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( CloudVert IN ) +{ + ConnectData OUT; + + OUT.hpos = mul(modelview, float4(IN.pos,1.0)); + // Offset the uv so we don't have a seam directly over our head. + float2 uv = IN.uv0 + float2( 0.5, 0.5 ); + + OUT.texCoord12.xy = uv * texScale.x; + OUT.texCoord12.xy += texOffset0; + + OUT.texCoord12.zw = uv * texScale.y; + OUT.texCoord12.zw += texOffset1; + + OUT.texCoord34.xy = uv * texScale.z; + OUT.texCoord34.xy += texOffset2; + + OUT.texCoord34.z = IN.pos.z; + OUT.texCoord34.w = 0.0; + + // Transform the normal, tangent and binormal vectors from object space to + // homogeneous projection space: + float3 vNormalWS = -IN.normal; + float3 vTangentWS = -IN.tangent; + float3 vBinormalWS = -IN.binormal; + + // Compute position in world space: + float4 vPositionWS = float4(IN.pos, 1.0) + float4(eyePosWorld, 1); //mul( IN.pos, objTrans ); + + // Compute and output the world view vector (unnormalized): + float3 vViewWS = eyePosWorld - vPositionWS.xyz; + + // Compute denormalized light vector in world space: + float3 vLightWS = -sunVec; + + // Normalize the light and view vectors and transform it to the tangent space: + float3x3 mWorldToTangent = float3x3( vTangentWS, vBinormalWS, vNormalWS ); + + // Propagate the view and the light vectors (in tangent space): + OUT.vLightTS = mul( vLightWS, mWorldToTangent ); + OUT.vViewTS = mul( mWorldToTangent, vViewWS ); + + OUT.worldDist = saturate( pow( max( IN.pos.z, 0 ), 2 ) ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureP.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureP.hlsl new file mode 100644 index 000000000..d0577428f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureP.hlsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +float4 main( Conn IN ) : TORQUE_TARGET0 +{ + return float4(IN.color.rgb, IN.color.a * TORQUE_TEX2D(diffuseMap, IN.texCoord).a); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureV.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureV.hlsl new file mode 100644 index 000000000..8bf4e88d8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/addColorTextureV.hlsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Appdata +{ + float3 position : POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +uniform float4x4 modelview; + +Conn main( Appdata In ) +{ + Conn Out; + Out.HPOS = mul(modelview, float4(In.position,1.0)); + Out.color = In.color; + Out.texCoord = In.texCoord; + return Out; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorP.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorP.hlsl new file mode 100644 index 000000000..dd9990e07 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorP.hlsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; +}; + +float4 main(Conn IN) : TORQUE_TARGET0 +{ + return IN.color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorV.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorV.hlsl new file mode 100644 index 000000000..d16dfb863 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/colorV.hlsl @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Appdata +{ + float3 position : POSITION; + float4 color : COLOR; +}; + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; +}; + +uniform float4x4 modelview; + +Conn main( Appdata In ) +{ + Conn Out; + Out.HPOS = mul(modelview, float4(In.position,1.0)); + Out.color = In.color; + return Out; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureP.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureP.glsl new file mode 100644 index 000000000..b9a10adf3 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureP.glsl @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D diffuseMap; +in vec4 color; +in vec2 texCoord; + +out vec4 OUT_col; + +void main() +{ + OUT_col = vec4(color.rgb, color.a * texture(diffuseMap, texCoord).a); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureV.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureV.glsl new file mode 100644 index 000000000..5d7f10168 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/addColorTextureV.glsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +uniform mat4 modelview; +out vec4 color; +out vec2 texCoord; + +void main() +{ + gl_Position = tMul(modelview, vPosition); + correctSSP(gl_Position); + color = vColor; + texCoord = vTexCoord0.st; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorP.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorP.glsl new file mode 100644 index 000000000..f9dfc3d4f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorP.glsl @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +in vec4 color; + +out vec4 OUT_col; + +void main() +{ + OUT_col = color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorV.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorV.glsl new file mode 100644 index 000000000..895917b55 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/colorV.glsl @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; + +uniform mat4 modelview; +out vec4 color; + +void main() +{ + gl_Position = tMul(modelview, vPosition); + correctSSP(gl_Position); + color = vColor; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureP.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureP.glsl new file mode 100644 index 000000000..c24b9db12 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureP.glsl @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D diffuseMap; +in vec4 color; +in vec2 texCoord; + +out vec4 OUT_col; + +void main() +{ + OUT_col = texture(diffuseMap, texCoord) * color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureV.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureV.glsl new file mode 100644 index 000000000..5d7f10168 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/modColorTextureV.glsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +uniform mat4 modelview; +out vec4 color; +out vec2 texCoord; + +void main() +{ + gl_Position = tMul(modelview, vPosition); + correctSSP(gl_Position); + color = vColor; + texCoord = vTexCoord0.st; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreP.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreP.glsl new file mode 100644 index 000000000..770f8904d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreP.glsl @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D colorTarget0Texture ; + +vec4 main( vec2 ScreenPos : VPOS ) : COLOR0 +{ + vec2 TexCoord = ScreenPos; + vec4 diffuse; + asm { tfetch2D diffuse, TexCoord, colorTarget0Texture, UnnormalizedTextureCoords = true }; + return diffuse; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreV.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreV.glsl new file mode 100644 index 000000000..e99d2e537 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/targetRestoreV.glsl @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureP.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureP.glsl new file mode 100644 index 000000000..50cef4bda --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureP.glsl @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D diffuseMap; +in vec2 texCoord; + +out vec4 OUT_col; + +void main() +{ + OUT_col = texture(diffuseMap, texCoord); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureV.glsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureV.glsl new file mode 100644 index 000000000..20dbb6f10 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/gl/textureV.glsl @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4 modelview; +out vec2 texCoord; + +void main() +{ + gl_Position = tMul(modelview, vPosition); + correctSSP(gl_Position); + texCoord = vTexCoord0.st; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureP.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureP.hlsl new file mode 100644 index 000000000..63afec2a4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureP.hlsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +float4 main( Conn IN ) : TORQUE_TARGET0 +{ + return TORQUE_TEX2D(diffuseMap, IN.texCoord) * IN.color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureV.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureV.hlsl new file mode 100644 index 000000000..8bf4e88d8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/modColorTextureV.hlsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Appdata +{ + float3 position : POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +struct Conn +{ + float4 HPOS : TORQUE_POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +uniform float4x4 modelview; + +Conn main( Appdata In ) +{ + Conn Out; + Out.HPOS = mul(modelview, float4(In.position,1.0)); + Out.color = In.color; + Out.texCoord = In.texCoord; + return Out; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreP.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreP.hlsl new file mode 100644 index 000000000..9ef44f426 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreP.hlsl @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D colorTarget0Texture : register(s0); + +float4 main( float2 ScreenPos : VPOS ) : COLOR0 +{ + float2 TexCoord = ScreenPos; + float4 diffuse; + asm { tfetch2D diffuse, TexCoord, colorTarget0Texture, UnnormalizedTextureCoords = true }; + return diffuse; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreV.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreV.hlsl new file mode 100644 index 000000000..3c4aefaec --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/targetRestoreV.hlsl @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +float4 main( const float2 inPosition : POSITION ) : POSITION +{ + return float4( inPosition, 0, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureP.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureP.hlsl new file mode 100644 index 000000000..82dbd4ce9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureP.hlsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; +}; + +float4 main(Conn IN) : TORQUE_TARGET0 +{ + return TORQUE_TEX2D(diffuseMap, IN.texCoord); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureV.hlsl b/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureV.hlsl new file mode 100644 index 000000000..204cf9514 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fixedFunction/textureV.hlsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +struct Appdata +{ + float3 position : POSITION; + float4 color : COLOR; + float2 texCoord : TEXCOORD0; +}; + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; +}; + +uniform float4x4 modelview; + +Conn main( Appdata In ) +{ + Conn Out; + Out.hpos = mul(modelview, float4(In.position, 1.0)); + Out.texCoord = In.texCoord; + return Out; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/foliage.hlsl b/Templates/BaseGame/game/data/shaders/common/foliage.hlsl new file mode 100644 index 000000000..9952c29d6 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/foliage.hlsl @@ -0,0 +1,186 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// CornerId corresponds to this arrangement +// from the perspective of the camera. +// +// 3 ---- 2 +// | | +// 0 ---- 1 +// + +#define MAX_COVERTYPES 8 + +uniform float2 gc_fadeParams; +uniform float2 gc_windDir; +uniform float3 gc_camRight; +uniform float3 gc_camUp; +uniform float4 gc_typeRects[MAX_COVERTYPES]; + +// .x = gust length +// .y = premultiplied simulation time and gust frequency +// .z = gust strength +uniform float3 gc_gustInfo; + +// .x = premultiplied simulation time and turbulance frequency +// .y = turbulance strength +uniform float2 gc_turbInfo; + + +static float sCornerRight[4] = { -0.5, 0.5, 0.5, -0.5 }; + +static float sCornerUp[4] = { 0, 0, 1, 1 }; + +static float sMovableCorner[4] = { 0, 0, 1, 1 }; + +static float2 sUVCornerExtent[4] = +{ + float2( 0, 1 ), + float2( 1, 1 ), + float2( 1, 0 ), + float2( 0, 0 ) +}; + + +/////////////////////////////////////////////////////////////////////////////// +// The following wind effect was derived from the GPU Gems 3 chapter... +// +// "Vegetation Procedural Animation and Shading in Crysis" +// by Tiago Sousa, Crytek +// + +float2 smoothCurve( float2 x ) +{ + return x * x * ( 3.0 - 2.0 * x ); +} + +float2 triangleWave( float2 x ) +{ + return abs( frac( x + 0.5 ) * 2.0 - 1.0 ); +} + +float2 smoothTriangleWave( float2 x ) +{ + return smoothCurve( triangleWave( x ) ); +} + +float windTurbulence( float bbPhase, float frequency, float strength ) +{ + // We create the input value for wave generation from the frequency and phase. + float2 waveIn = bbPhase.xx + frequency.xx; + + // We use two square waves to generate the effect which + // is then scaled by the overall strength. + float2 waves = ( frac( waveIn.xy * float2( 1.975, 0.793 ) ) * 2.0 - 1.0 ); + waves = smoothTriangleWave( waves ); + + // Sum up the two waves into a single wave. + return ( waves.x + waves.y ) * strength; +} + +float2 windEffect( float bbPhase, + float2 windDirection, + float gustLength, + float gustFrequency, + float gustStrength, + float turbFrequency, + float turbStrength ) +{ + // Calculate the ambient wind turbulence. + float turbulence = windTurbulence( bbPhase, turbFrequency, turbStrength ); + + // We simulate the overall gust via a sine wave. + float gustPhase = clamp( sin( ( bbPhase - gustFrequency ) / gustLength ) , 0, 1 ); + float gustOffset = ( gustPhase * gustStrength ) + ( ( 0.2 + gustPhase ) * turbulence ); + + // Return the final directional wind effect. + return gustOffset.xx * windDirection.xy; +} + +void foliageProcessVert( inout float3 position, + inout float4 diffuse, + inout float4 texCoord, + inout float3 normal, + inout float3 T, + in float3 eyePos ) +{ + // Assign the normal and tagent values. + //normal = float3( 0, 0, 1 );//cross( gc_camUp, gc_camRight ); + T = gc_camRight; + + // Pull out local vars we need for work. + int corner = ( diffuse.a * 255.0f ) + 0.5f; + float2 size = texCoord.xy; + int type = texCoord.z; + + // The billboarding is based on the camera direction. + float3 rightVec = gc_camRight * sCornerRight[corner]; + float3 upVec = gc_camUp * sCornerUp[corner]; + + // Figure out the corner position. + float3 outPos = ( upVec * size.y ) + ( rightVec * size.x ); + float len = length( outPos.xyz ); + + // We derive the billboard phase used for wind calculations from its position. + float bbPhase = dot( position.xyz, 1 ); + + // Get the overall wind gust and turbulence effects. + float3 wind; + wind.xy = windEffect( bbPhase, + gc_windDir, + gc_gustInfo.x, gc_gustInfo.y, gc_gustInfo.z, + gc_turbInfo.x, gc_turbInfo.y ); + wind.z = 0; + + // Add the summed wind effect into the point. + outPos.xyz += wind.xyz * texCoord.w; + + // Do a simple spherical clamp to keep the foliage + // from stretching too much by wind effect. + outPos.xyz = normalize( outPos.xyz ) * len; + + // Move the point into world space. + position += outPos; + + // Grab the uv set and setup the texture coord. + float4 uvSet = gc_typeRects[type]; + texCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + texCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + + // Animate the normal to get lighting changes + // across the the wind swept foliage. + // + // TODO: Expose the 10x as a factor to control + // how much the wind effects the lighting on the grass. + // + normal.xy += wind.xy * ( 10.0 * texCoord.w ); + normal = normalize( normal ); + + // Get the alpha fade value. + + float fadeStart = gc_fadeParams.x; + float fadeEnd = gc_fadeParams.y; + const float fadeRange = fadeEnd - fadeStart; + + float dist = distance( eyePos, position.xyz ) - fadeStart; + diffuse.a = 1 - clamp( dist / fadeRange, 0, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorP.hlsl b/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorP.hlsl new file mode 100644 index 000000000..a8bb68e28 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorP.hlsl @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shdrConsts.h" +#include "shaderModel.hlsl" +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 outTexCoord : TEXCOORD0; + float4 color : COLOR0; + float4 groundAlphaCoeff : COLOR1; + float2 alphaLookup : TEXCOORD1; +}; + +struct Fragout +{ + float4 col : TORQUE_TARGET0; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); +TORQUE_UNIFORM_SAMPLER2D(alphaMap, 1); + +uniform float4 groundAlpha; +uniform float4 ambient; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main( ConnectData IN ) +{ + Fragout OUT; + + float4 alpha = TORQUE_TEX2D(alphaMap, IN.alphaLookup); + OUT.col = float4( ambient.rgb * IN.lum.rgb, 1.0 ) * TORQUE_TEX2D(diffuseMap, IN.texCoord); + OUT.col.a = OUT.col.a * min(alpha, groundAlpha + IN.groundAlphaCoeff.x).x; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorV.hlsl b/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorV.hlsl new file mode 100644 index 000000000..70ec9ff4c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/fxFoliageReplicatorV.hlsl @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct VertData +{ + float3 position : POSITION; + float3 normal : NORMAL; + float2 texCoord : TEXCOORD0; + float2 waveScale : TEXCOORD1; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 outTexCoord : TEXCOORD0; + float4 color : COLOR0; + float4 groundAlphaCoeff : COLOR1; + float2 alphaLookup : TEXCOORD1; +}; + +uniform float4x4 projection : register(C0); +uniform float4x4 world : register(C4); +uniform float GlobalSwayPhase : register(C8); +uniform float SwayMagnitudeSide : register(C9); +uniform float SwayMagnitudeFront : register(C10); +uniform float GlobalLightPhase : register(C11); +uniform float LuminanceMagnitude : register(C12); +uniform float LuminanceMidpoint : register(C13); +uniform float DistanceRange : register(C14); +uniform float3 CameraPos : register(C15); +uniform float TrueBillboard : register(C16); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertData IN ) +{ + ConnectData OUT; + + // Init a transform matrix to be used in the following steps + float4x4 trans = 0; + trans[0][0] = 1; + trans[1][1] = 1; + trans[2][2] = 1; + trans[3][3] = 1; + trans[0][3] = IN.position.x; + trans[1][3] = IN.position.y; + trans[2][3] = IN.position.z; + + // Billboard transform * world matrix + float4x4 o = world; + o = mul(o, trans); + + // Keep only the up axis result and position transform. + // This gives us "cheating" cylindrical billboarding. + o[0][0] = 1; + o[1][0] = 0; + o[2][0] = 0; + o[3][0] = 0; + o[0][1] = 0; + o[1][1] = 1; + o[2][1] = 0; + o[3][1] = 0; + + // Unless the user specified TrueBillboard, + // in which case we want the z axis to also be camera facing. + +#ifdef TRUE_BILLBOARD + + o[0][2] = 0; + o[1][2] = 0; + o[2][2] = 1; + o[3][2] = 0; + +#endif + + // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, + // the y coordinate determines if this vertex actually sways or not. + float xSway, ySway; + float wavePhase = GlobalSwayPhase * IN.waveScale.x; + sincos(wavePhase, ySway, xSway); + xSway = xSway * IN.waveScale.y * SwayMagnitudeSide; + ySway = ySway * IN.waveScale.y * SwayMagnitudeFront; + float4 p; + p = mul(o, float4(IN.normal.x + xSway, ySway, IN.normal.z, 1)); + + // Project the point + OUT.hpos = mul(projection, p); + + // Lighting + float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + IN.normal.y); + + // Alpha + float3 worldPos = IN.position; + float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange; + alpha = clamp(alpha, 0.0f, 1.0f); //pass it through + + OUT.alphaLookup = float2(alpha, 0.0f); + OUT.groundAlphaCoeff = all(IN.normal.z); + OUT.outTexCoord = IN.texCoord; + OUT.color = float4(Luminance, Luminance, Luminance, 1.0f); + + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsP.glsl new file mode 100644 index 000000000..5b3f50519 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsP.glsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.glsl" +#include "hlslCompat.glsl" + +//ConnectData +in vec2 texCoord; +#define IN_texCoord texCoord + + +uniform sampler2D diffuseMap ; + +out vec4 OUT_col; + +void main() +{ + vec4 col = texture( diffuseMap, IN_texCoord ); + OUT_col = hdrEncode( col ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsV.glsl new file mode 100644 index 000000000..cccbafa8c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/basicCloudsV.glsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +//CloudVert +in vec4 vPosition; +in vec2 vTexCoord0; + +#define IN_pos vPosition +#define IN_uv0 vTexCoord0 + +uniform mat4 modelview; +uniform float accumTime; +uniform float texScale; +uniform vec2 texDirection; +uniform vec2 texOffset; + +out vec2 texCoord; +#define OUT_texCoord texCoord + +void main() +{ + gl_Position = tMul(modelview, IN_pos); + + vec2 uv = IN_uv0; + uv += texOffset; + uv *= texScale; + uv += accumTime * texDirection; + + OUT_texCoord = uv; + + correctSSP(gl_Position); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/blurP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/blurP.glsl new file mode 100644 index 000000000..a27538762 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/blurP.glsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//***************************************************************************** +// Glow Shader +//***************************************************************************** +uniform vec4 kernel; +uniform sampler2D diffuseMap; + +in vec2 texc0, texc1, texc2, texc3; + +out vec4 OUT_col; + +void main() +{ + OUT_col = texture(diffuseMap, texc0) * kernel.x; + OUT_col += texture(diffuseMap, texc1) * kernel.y; + OUT_col += texture(diffuseMap, texc2) * kernel.z; + OUT_col += texture(diffuseMap, texc3) * kernel.w; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/blurV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/blurV.glsl new file mode 100644 index 000000000..1bfb0cd1b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/blurV.glsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//***************************************************************************** +// Glow shader +//***************************************************************************** + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +uniform mat4 modelview; +uniform vec2 offset0, offset1, offset2, offset3; + +out vec2 texc0, texc1, texc2, texc3; + +void main() +{ + gl_Position = modelview * vPosition; + + vec2 tc = vTexCoord0.st; + tc.y = 1.0 - tc.y; + + texc0 = tc + offset0; + texc1 = tc + offset1; + texc2 = tc + offset2; + texc3 = tc + offset3; + gl_Position.y *= -1; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerP.glsl new file mode 100644 index 000000000..877a132da --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerP.glsl @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" +#include "torque.glsl" +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +//ConnectData +in vec4 texCoord12; +#define IN_texCoord12 texCoord12 +in vec4 texCoord34; +#define IN_texCoord34 texCoord34 +in vec3 vLightTS; // light vector in tangent space, denormalized +#define IN_vLightTS vLightTS +in vec3 vViewTS; // view vector in tangent space, denormalized +#define IN_vViewTS vViewTS +in float worldDist; +#define IN_worldDist worldDist + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform sampler2D normalHeightMap; +uniform vec3 ambientColor; +uniform vec3 sunColor; +uniform float cloudCoverage; +uniform vec3 cloudBaseColor; +uniform float cloudExposure; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- +// The per-color weighting to be used for luminance calculations in RGB order. +const vec3 LUMINANCE_VECTOR = vec3(0.2125f, 0.7154f, 0.0721f); + + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + +// Calculates the Rayleigh phase function +float getRayleighPhase( float angle ) +{ + return 0.75 * ( 1.0 + pow( angle, 2.0 ) ); +} + +// Returns the output rgb color given a texCoord and parameters it uses +// for lighting calculation. +vec3 ComputeIllumination( vec2 texCoord, + vec3 vLightTS, + vec3 vViewTS, + vec3 vNormalTS ) +{ + //return noiseNormal; + //return vNormalTS; + + vec3 vLightTSAdj = vec3( -vLightTS.x, -vLightTS.y, vLightTS.z ); + + float dp = dot( vNormalTS, vLightTSAdj ); + + // Calculate the amount of illumination (lightTerm)... + + // We do both a rim lighting effect and a halfLambertian lighting effect + // and combine the result. + float halfLambertTerm = clamp( pow( dp * 0.5 + 0.5, 1.0 ), 0.0, 1.0 ); + float rimLightTerm = pow( ( 1.0 - dp ), 1.0 ); + float lightTerm = clamp( halfLambertTerm * 1.0 + rimLightTerm * dp, 0.0, 1.0 ); + lightTerm *= 0.5; + + // Use a simple RayleighPhase function to simulate single scattering towards + // the camera. + float angle = dot( vLightTS, vViewTS ); + lightTerm *= getRayleighPhase( angle ); + + // Combine terms and colors into the output color. + //vec3 lightColor = ( lightTerm * sunColor * fOcclusionShadow ) + ambientColor; + vec3 lightColor = mix( ambientColor, sunColor, lightTerm ); + //lightColor = mix( lightColor, ambientColor, cloudCoverage ); + vec3 finalColor = cloudBaseColor * lightColor; + + return finalColor; +} + +void main() +{ + // Normalize the interpolated vectors: + vec3 vViewTS = normalize( vViewTS ); + vec3 vLightTS = normalize( vLightTS ); + + vec4 cResultColor = vec4( 0, 0, 0, 1 ); + + vec2 texSample = IN_texCoord12.xy; + + vec4 noise1 = texture( normalHeightMap, IN_texCoord12.zw ); + noise1 = normalize( ( noise1 - 0.5 ) * 2.0 ); + //return noise1; + + vec4 noise2 = texture( normalHeightMap, IN_texCoord34.xy ); + noise2 = normalize( ( noise2 - 0.5 ) * 2.0 ); + //return noise2; + + vec3 noiseNormal = normalize( noise1 + noise2 ).xyz; + //return vec4( noiseNormal, 1.0 ); + + float noiseHeight = noise1.a * noise2.a * ( cloudCoverage / 2.0 + 0.5 ); + + vec3 vNormalTS = normalize( texture( normalHeightMap, texSample ).xyz * 2.0 - 1.0 ); + vNormalTS += noiseNormal; + vNormalTS = normalize( vNormalTS ); + + // Compute resulting color for the pixel: + cResultColor.rgb = ComputeIllumination( texSample, vLightTS, vViewTS, vNormalTS ); + + float coverage = ( cloudCoverage - 0.5 ) * 2.0; + cResultColor.a = texture( normalHeightMap, texSample ).a + coverage + noiseHeight; + + if ( cloudCoverage > -1.0 ) + cResultColor.a /= 1.0 + coverage; + + cResultColor.a = clamp( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ), 0.0, 1.0 ); + + cResultColor.a = mix( cResultColor.a, 0.0, 1.0 - pow(IN_worldDist,2.0) ); + + OUT_col = hdrEncode(cResultColor); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerV.glsl new file mode 100644 index 000000000..395c6f286 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/cloudLayerV.glsl @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec3 vNormal; +in vec3 vBinormal; +in vec3 vTangent; +in vec2 vTexCoord0; + +out vec4 texCoord12; +#define OUT_texCoord12 texCoord12 +out vec4 texCoord34; +#define OUT_texCoord34 texCoord34 +out vec3 vLightTS; // light vector in tangent space, denormalized +#define OUT_vLightTS vLightTS +out vec3 vViewTS; // view vector in tangent space, denormalized +#define OUT_vViewTS vViewTS +out float worldDist; +#define OUT_worldDist worldDist + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform mat4 modelview; +uniform vec3 eyePosWorld; +uniform vec3 sunVec; +uniform vec2 texOffset0; +uniform vec2 texOffset1; +uniform vec2 texOffset2; +uniform vec3 texScale; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec4 IN_pos = vPosition; + vec3 IN_normal = vNormal; + vec3 IN_binormal = vBinormal; + vec3 IN_tangent = vTangent; + vec2 IN_uv0 = vTexCoord0.st; + + gl_Position = modelview * IN_pos; + + // Offset the uv so we don't have a seam directly over our head. + vec2 uv = IN_uv0 + vec2( 0.5, 0.5 ); + + OUT_texCoord12.xy = uv * texScale.x; + OUT_texCoord12.xy += texOffset0; + + OUT_texCoord12.zw = uv * texScale.y; + OUT_texCoord12.zw += texOffset1; + + OUT_texCoord34.xy = uv * texScale.z; + OUT_texCoord34.xy += texOffset2; + + OUT_texCoord34.z = IN_pos.z; + OUT_texCoord34.w = 0.0; + + // Transform the normal, tangent and binormal vectors from object space to + // homogeneous projection space: + vec3 vNormalWS = -IN_normal; + vec3 vTangentWS = -IN_tangent; + vec3 vBinormalWS = -IN_binormal; + + // Compute position in world space: + vec4 vPositionWS = IN_pos + vec4( eyePosWorld, 1 ); //tMul( IN_pos, objTrans ); + + // Compute and output the world view vector (unnormalized): + vec3 vViewWS = eyePosWorld - vPositionWS.xyz; + + // Compute denormalized light vector in world space: + vec3 vLightWS = -sunVec; + + // Normalize the light and view vectors and transform it to the IN_tangent space: + mat3 mWorldToTangent = mat3( vTangentWS, vBinormalWS, vNormalWS ); + + // Propagate the view and the light vectors (in tangent space): + OUT_vLightTS = vLightWS * mWorldToTangent; + OUT_vViewTS = mWorldToTangent * vViewWS; + + OUT_worldDist = clamp( pow( max( IN_pos.z, 0 ), 2 ), 0.0, 1.0 ); + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/foliage.glsl b/Templates/BaseGame/game/data/shaders/common/gl/foliage.glsl new file mode 100644 index 000000000..38b66e767 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/foliage.glsl @@ -0,0 +1,186 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// CornerId corresponds to this arrangement +// from the perspective of the camera. +// +// 3 ---- 2 +// | | +// 0 ---- 1 +// + +#define MAX_COVERTYPES 8 + +uniform vec3 gc_camRight; +uniform vec3 gc_camUp; +uniform vec4 gc_typeRects[MAX_COVERTYPES]; +uniform vec2 gc_fadeParams; +uniform vec2 gc_windDir; + +// .x = gust length +// .y = premultiplied simulation time and gust frequency +// .z = gust strength +uniform vec3 gc_gustInfo; + +// .x = premultiplied simulation time and turbulance frequency +// .y = turbulance strength +uniform vec2 gc_turbInfo; + + +const float sCornerRight[4] = float[]( -0.5, 0.5, 0.5, -0.5 ); + +const float sCornerUp[4] = float[]( 0, 0, 1, 1 ); + +const float sMovableCorner[4] = float[]( 0, 0, 1, 1 ); + +const vec2 sUVCornerExtent[4] = vec2[] +( + vec2( 0, 1 ), + vec2( 1, 1 ), + vec2( 1, 0 ), + vec2( 0, 0 ) +); + + +/////////////////////////////////////////////////////////////////////////////// +// The following wind effect was derived from the GPU Gems 3 chapter... +// +// "Vegetation Procedural Animation and Shading in Crysis" +// by Tiago Sousa, Crytek +// + +vec2 smoothCurve( vec2 x ) +{ + return x * x * ( 3.0 - 2.0 * x ); +} + +vec2 triangleWave( vec2 x ) +{ + return abs( fract( x + 0.5 ) * 2.0 - 1.0 ); +} + +vec2 smoothTriangleWave( vec2 x ) +{ + return smoothCurve( triangleWave( x ) ); +} + +float windTurbulence( float bbPhase, float frequency, float strength ) +{ + // We create the input value for wave generation from the frequency and phase. + vec2 waveIn = vec2( bbPhase + frequency ); + + // We use two square waves to generate the effect which + // is then scaled by the overall strength. + vec2 waves = ( fract( waveIn.xy * vec2( 1.975, 0.793 ) ) * 2.0 - 1.0 ); + waves = smoothTriangleWave( waves ); + + // Sum up the two waves into a single wave. + return ( waves.x + waves.y ) * strength; +} + +vec2 windEffect( float bbPhase, + vec2 windDirection, + float gustLength, + float gustFrequency, + float gustStrength, + float turbFrequency, + float turbStrength ) +{ + // Calculate the ambient wind turbulence. + float turbulence = windTurbulence( bbPhase, turbFrequency, turbStrength ); + + // We simulate the overall gust via a sine wave. + float gustPhase = clamp( sin( ( bbPhase - gustFrequency ) / gustLength ) , 0.0, 1.0 ); + float gustOffset = ( gustPhase * gustStrength ) + ( ( 0.2 + gustPhase ) * turbulence ); + + // Return the final directional wind effect. + return vec2(gustOffset) * windDirection.xy; +} + +void foliageProcessVert( inout vec3 position, + inout vec4 diffuse, + inout vec4 texCoord, + inout vec3 normal, + inout vec3 T, + in vec3 eyePos ) +{ + // Assign the normal and tagent values. + //normal = vec3( 0, 0, 1 );//cross( gc_camUp, gc_camRight ); + T = gc_camRight; + + // Pull out local vars we need for work. + int corner = int( ( diffuse.a * 255.0 ) + 0.5 ); + vec2 size = texCoord.xy; + int type = int( texCoord.z ); + + // The billboarding is based on the camera direction. + vec3 rightVec = gc_camRight * sCornerRight[corner]; + vec3 upVec = gc_camUp * sCornerUp[corner]; + + // Figure out the corner position. + vec3 outPos = ( upVec * size.y ) + ( rightVec * size.x ); + float len = length( outPos.xyz ); + + // We derive the billboard phase used for wind calculations from its position. + float bbPhase = dot( position.xyz, vec3( 1.0 ) ); + + // Get the overall wind gust and turbulence effects. + vec3 wind; + wind.xy = windEffect( bbPhase, + gc_windDir, + gc_gustInfo.x, gc_gustInfo.y, gc_gustInfo.z, + gc_turbInfo.x, gc_turbInfo.y ); + wind.z = 0.0; + + // Add the summed wind effect into the point. + outPos.xyz += wind.xyz * texCoord.w; + + // Do a simple spherical clamp to keep the foliage + // from stretching too much by wind effect. + outPos.xyz = normalize( outPos.xyz ) * len; + + // Move the point into world space. + position += outPos; + + // Grab the uv set and setup the texture coord. + vec4 uvSet = gc_typeRects[type]; + texCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + texCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + + // Animate the normal to get lighting changes + // across the the wind swept foliage. + // + // TODO: Expose the 10x as a factor to control + // how much the wind effects the lighting on the grass. + // + normal.xy += wind.xy * ( 10.0 * texCoord.w ); + normal = normalize( normal ); + + // Get the alpha fade value. + + float fadeStart = gc_fadeParams.x; + float fadeEnd = gc_fadeParams.y; + float fadeRange = fadeEnd - fadeStart; + + float dist = distance( eyePos, position.xyz ) - fadeStart; + diffuse.a = 1.0 - clamp( dist / fadeRange, 0.0, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorP.glsl new file mode 100644 index 000000000..b4d591486 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorP.glsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +uniform sampler2D diffuseMap, alphaMap; +uniform vec4 groundAlpha; + +in vec4 color, groundAlphaCoeff; +in vec2 outTexCoord, alphaLookup; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec4 alpha = texture(alphaMap, alphaLookup); + OUT_col = color * texture(diffuseMap, outTexCoord); + OUT_col.a = OUT_col.a * min(alpha, groundAlpha + groundAlphaCoeff.x).x; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorV.glsl new file mode 100644 index 000000000..c8dcf1ddb --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/fxFoliageReplicatorV.glsl @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; +in vec2 vTexCoord2; + +uniform mat4 projection, world; +uniform vec3 CameraPos; +uniform float GlobalSwayPhase, SwayMagnitudeSide, SwayMagnitudeFront, + GlobalLightPhase, LuminanceMagnitude, LuminanceMidpoint, DistanceRange; + +out vec4 color, groundAlphaCoeff; +out vec2 outTexCoord, alphaLookup; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + // Init a transform matrix to be used in the following steps + mat4 trans = mat4(0.0); + trans[0][0] = 1.0; + trans[1][1] = 1.0; + trans[2][2] = 1.0; + trans[3][3] = 1.0; + trans[3][0] = vPosition.x; + trans[3][1] = vPosition.y; + trans[3][2] = vPosition.z; + + // Billboard transform * world matrix + mat4 o = world; + o = o * trans; + + // Keep only the up axis result and position transform. + // This gives us "cheating" cylindrical billboarding. + o[0][0] = 1.0; + o[0][1] = 0.0; + o[0][2] = 0.0; + o[0][3] = 0.0; + o[1][0] = 0.0; + o[1][1] = 1.0; + o[1][2] = 0.0; + o[1][3] = 0.0; + + // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, + // the y coordinate determines if this vertex actually sways or not. + float xSway, ySway; + float wavePhase = GlobalSwayPhase * vTexCoord1.x; + ySway = sin(wavePhase); + xSway = cos(wavePhase); + xSway = xSway * vTexCoord1.y * SwayMagnitudeSide; + ySway = ySway * vTexCoord1.y * SwayMagnitudeFront; + vec4 p; + p = o * vec4(vNormal.x + xSway, ySway, vNormal.z, 1.0); + + // Project the point + gl_Position = projection * p; + + // Lighting + float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + vNormal.y); + + // Alpha + vec3 worldPos = vec3(vPosition.x, vPosition.y, vPosition.z); + float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange; + alpha = clamp(alpha, 0.0, 1.0); //pass it through + + alphaLookup = vec2(alpha, 0.0); + bool alphaCoeff = bool(vNormal.z); + groundAlphaCoeff = vec4(float(alphaCoeff)); + outTexCoord = vTexCoord0.st; + color = vec4(Luminance, Luminance, Luminance, 1.0); + gl_Position.y *= -1; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/guiMaterialV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/guiMaterialV.glsl new file mode 100644 index 000000000..de3845ee7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/guiMaterialV.glsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4x4 modelview; + +out vec4 hpos; +out vec2 uv0; + + +void main() +{ + hpos = vec4( modelview * vPosition ); + gl_Position = hpos; + + uv0 = vTexCoord0.st; + gl_Position.y *= -1; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/hlslCompat.glsl b/Templates/BaseGame/game/data/shaders/common/gl/hlslCompat.glsl new file mode 100644 index 000000000..c8fe73620 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/hlslCompat.glsl @@ -0,0 +1,103 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// These are some simple wrappers for simple +// HLSL compatibility. + +#define float4 vec4 +#define float3 vec3 +#define float2 vec2 + +#define half float +#define half2 vec2 +#define half3 vec3 +#define half4 vec4 + +#define float4x4 mat4 +#define float3x3 mat3 +#define float2x2 mat2 + +#define texCUBE texture +#define tex2D texture +#define tex1D texture +#define tex2Dproj textureProj +#define tex2Dlod( sampler, texCoord ) textureLod(sampler, texCoord.xy, texCoord.w) + +#define samplerCUBE samplerCube + +#define frac fract + +#define lerp mix + +void tSetMatrixRow(inout float3x3 m, int row, float3 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; +} + +void tSetMatrixRow(inout float4x4 m, int row, float4 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; + m[3][row] = value.w; +} + +#define tGetMatrix3Row(matrix, row) float3(matrix[0][row], matrix[1][row], matrix[2][row]) +#define tGetMatrix4Row(matrix, row) float4(matrix[0][row], matrix[1][row], matrix[2][row], matrix[3][row]) + +float3x3 float4x4to3x3(float4x4 m) +{ + return float3x3( vec3(m[0]).xyz, m[1].xyz, m[2].xyz); +} + +float3x3 float4x4to3x3_(float4x4 m) +{ + return float3x3( vec3(m[0]), m[1].xyz, m[2].xyz); +} + +mat4 mat4FromRow( float r0c0, float r0c1, float r0c2, float r0c3, + float r1c0, float r1c1, float r1c2, float r1c3, + float r2c0, float r2c1, float r2c2, float r2c3, + float r3c0, float r3c1, float r3c2, float r3c3 ) +{ + return mat4( r0c0, r1c0, r2c0, r3c0, + r0c1, r1c1, r2c1, r3c1, + r0c2, r1c2, r2c2, r3c2, + r0c3, r1c3, r2c3, r3c3 ); +} + + +#define saturate( val ) clamp( val, 0.0, 1.0 ) + +#define round( n ) (sign( n ) * floor( abs( n ) + 0.5 )) + +#define tMul(a, b) (a*b) + +#define inversesqrt( n ) inversesqrt( n ) + +#define correctSSP(vec) vec.y *= -1 + +#ifdef TORQUE_PIXEL_SHADER + void clip(float a) { if(a < 0) discard;} +#endif diff --git a/Templates/BaseGame/game/data/shaders/common/gl/imposter.glsl b/Templates/BaseGame/game/data/shaders/common/gl/imposter.glsl new file mode 100644 index 000000000..20bc62688 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/imposter.glsl @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.glsl" + + +#define IMPOSTER_MAX_UVS 64 + + +void imposter_v( + // These parameters usually come from the vertex. + vec3 center, + int corner, + float halfSize, + vec3 imposterUp, + vec3 imposterRight, + + // These are from the imposter shader constant. + int numEquatorSteps, + int numPolarSteps, + float polarAngle, + bool includePoles, + + // Other shader constants. + vec3 camPos, + vec4 uvs[IMPOSTER_MAX_UVS], + + // The outputs of this function. + out vec3 outWsPosition, + out vec2 outTexCoord, + out mat3 outWorldToTangent + ) +{ + + float M_HALFPI_F = 1.57079632679489661923; + float M_PI_F = 3.14159265358979323846; + float M_2PI_F = 6.28318530717958647692; + + + float sCornerRight[4];// = float[]( -1.0, 1.0, 1.0, -1.0 ); + sCornerRight[0] = -1.0; + sCornerRight[1] = 1.0; + sCornerRight[2] = 1.0; + sCornerRight[3] = -1.0; + float sCornerUp[4];// = float[]( -1.0, -1.0, 1.0, 1.0 ); + sCornerUp[0] = -1.0; + sCornerUp[1] = -1.0; + sCornerUp[2] = 1.0; + sCornerUp[3] = 1.0; + vec2 sUVCornerExtent[4];// = vec2[](vec2( 0.0, 1.0 ), vec2( 1.0, 1.0 ), vec2( 1.0, 0.0 ), vec2( 0.0, 0.0 )); + sUVCornerExtent[0] = vec2( 0.0, 1.0 ); + sUVCornerExtent[1] = vec2( 1.0, 1.0 ); + sUVCornerExtent[2] = vec2( 1.0, 0.0 ); + sUVCornerExtent[3] = vec2( 0.0, 0.0 ); + + // TODO: This could all be calculated on the CPU. + float equatorStepSize = M_2PI_F / float( numEquatorSteps ); + float equatorHalfStep = ( equatorStepSize / 2.0 ) - 0.0001; + float polarStepSize = M_PI_F / float( numPolarSteps ); + float polarHalfStep = ( polarStepSize / 2.0 ) - 0.0001; + + // The vector between the camera and the billboard. + vec3 lookVec = normalize( camPos - center ); + + // Generate the camera up and right vectors from + // the object transform and camera forward. + vec3 camUp = imposterUp; + vec3 camRight = cross( -lookVec, camUp ); + + // The billboarding is based on the camera directions. + vec3 rightVec = camRight * sCornerRight[corner]; + vec3 upVec = camUp * sCornerUp[corner]; + + float lookPitch = acos( dot( imposterUp, lookVec ) ); + + // First check to see if we need to render the top billboard. + int index; + /* + if ( includePoles && ( lookPitch < polarAngle || lookPitch > sPi - polarAngle ) ) + { + index = numEquatorSteps * 3; + + // When we render the top/bottom billboard we always use + // a fixed vector that matches the rotation of the object. + rightVec = vec3( 1, 0, 0 ) * sCornerRight[corner]; + upVec = vec3( 0, 1, 0 ) * sCornerUp[corner]; + + if ( lookPitch > sPi - polarAngle ) + { + upVec = -upVec; + index++; + } + } + else + */ + { + // Calculate the rotation around the z axis then add the + // equator half step. This gets the images to switch a + // half step before the captured angle is met. + float lookAzimuth = atan( lookVec.y, lookVec.x ); + float azimuth = atan( imposterRight.y, imposterRight.x ); + float rotZ = ( lookAzimuth - azimuth ) + equatorHalfStep; + + // The y rotation is calculated from the look vector and + // the object up vector. + float rotY = lookPitch - polarHalfStep; + + // TODO: How can we do this without conditionals? + // Normalize the result to 0 to 2PI. + if ( rotZ < 0.0 ) + rotZ += M_2PI_F; + if ( rotZ > M_2PI_F ) + rotZ -= M_2PI_F; + if ( rotY < 0.0 ) + rotY += M_2PI_F; + if ( rotY > M_PI_F ) // Not M_2PI_F? + rotY -= M_2PI_F; + + float polarIdx = round( abs( rotY ) / polarStepSize ); + + // Get the index to the start of the right polar + // images for this viewing angle. + int numPolarOffset = int( float( numEquatorSteps ) * polarIdx ); + + // Calculate the final image index for lookup + // of the texture coords. + index = int( rotZ / equatorStepSize ) + numPolarOffset; + } + + // Generate the final world space position. + outWsPosition = center + ( upVec * halfSize ) + ( rightVec * halfSize ); + + // Grab the uv set and setup the texture coord. + vec4 uvSet = uvs[index]; + outTexCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + outTexCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + + // Needed for normal mapping and lighting. + outWorldToTangent[0] = vec3( 1, 0, 0 ); + outWorldToTangent[1] = vec3( 0, 1, 0 ); + outWorldToTangent[2] = vec3( 0, 0, -1 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/lighting.glsl b/Templates/BaseGame/game/data/shaders/common/gl/lighting.glsl new file mode 100644 index 000000000..804ab1e3b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/lighting.glsl @@ -0,0 +1,249 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./torque.glsl" + +#ifndef TORQUE_SHADERGEN + +// These are the uniforms used by most lighting shaders. + +uniform vec4 inLightPos[3]; +uniform vec4 inLightInvRadiusSq; +uniform vec4 inLightColor[4]; + +#ifndef TORQUE_BL_NOSPOTLIGHT + uniform vec4 inLightSpotDir[3]; + uniform vec4 inLightSpotAngle; + uniform vec4 inLightSpotFalloff; +#endif + +uniform vec4 ambient; +#define ambientCameraFactor 0.3 +uniform float specularPower; +uniform vec4 specularColor; + +#endif // !TORQUE_SHADERGEN + + +void compute4Lights( vec3 wsView, + vec3 wsPosition, + vec3 wsNormal, + vec4 shadowMask, + + #ifdef TORQUE_SHADERGEN + + vec4 inLightPos[3], + vec4 inLightInvRadiusSq, + vec4 inLightColor[4], + vec4 inLightSpotDir[3], + vec4 inLightSpotAngle, + vec4 inLightSpotFalloff, + float specularPower, + vec4 specularColor, + + #endif // TORQUE_SHADERGEN + + out vec4 outDiffuse, + out vec4 outSpecular ) +{ + // NOTE: The light positions and spotlight directions + // are stored in SoA order, so inLightPos[0] is the + // x coord for all 4 lights... inLightPos[1] is y... etc. + // + // This is the key to fully utilizing the vector units and + // saving a huge amount of instructions. + // + // For example this change saved more than 10 instructions + // over a simple for loop for each light. + + int i; + + vec4 lightVectors[3]; + for ( i = 0; i < 3; i++ ) + lightVectors[i] = wsPosition[i] - inLightPos[i]; + + vec4 squareDists = vec4(0); + for ( i = 0; i < 3; i++ ) + squareDists += lightVectors[i] * lightVectors[i]; + + // Accumulate the dot product between the light + // vector and the normal. + // + // The normal is negated because it faces away from + // the surface and the light faces towards the + // surface... this keeps us from needing to flip + // the light vector direction which complicates + // the spot light calculations. + // + // We normalize the result a little later. + // + vec4 nDotL = vec4(0); + for ( i = 0; i < 3; i++ ) + nDotL += lightVectors[i] * -wsNormal[i]; + + vec4 rDotL = vec4(0); + #ifndef TORQUE_BL_NOSPECULAR + + // We're using the Phong specular reflection model + // here where traditionally Torque has used Blinn-Phong + // which has proven to be more accurate to real materials. + // + // We do so because its cheaper as do not need to + // calculate the half angle for all 4 lights. + // + // Advanced Lighting still uses Blinn-Phong, but the + // specular reconstruction it does looks fairly similar + // to this. + // + vec3 R = reflect( wsView, -wsNormal ); + + for ( i = 0; i < 3; i++ ) + rDotL += lightVectors[i] * R[i]; + + #endif + + // Normalize the dots. + // + // Notice we're using the half type here to get a + // much faster sqrt via the rsq_pp instruction at + // the loss of some precision. + // + // Unless we have some extremely large point lights + // i don't believe the precision loss will matter. + // + half4 correction = half4(inversesqrt( squareDists )); + nDotL = saturate( nDotL * correction ); + rDotL = clamp( rDotL * correction, 0.00001, 1.0 ); + + // First calculate a simple point light linear + // attenuation factor. + // + // If this is a directional light the inverse + // radius should be greater than the distance + // causing the attenuation to have no affect. + // + vec4 atten = saturate( 1.0 - ( squareDists * inLightInvRadiusSq ) ); + + #ifndef TORQUE_BL_NOSPOTLIGHT + + // The spotlight attenuation factor. This is really + // fast for what it does... 6 instructions for 4 spots. + + vec4 spotAtten = vec4(0); + for ( i = 0; i < 3; i++ ) + spotAtten += lightVectors[i] * inLightSpotDir[i]; + + vec4 cosAngle = ( spotAtten * correction ) - inLightSpotAngle; + atten *= saturate( cosAngle * inLightSpotFalloff ); + + #endif + + // Finally apply the shadow masking on the attenuation. + atten *= shadowMask; + + // Get the final light intensity. + vec4 intensity = nDotL * atten; + + // Combine the light colors for output. + outDiffuse = vec4(0); + for ( i = 0; i < 4; i++ ) + outDiffuse += intensity[i] * inLightColor[i]; + + // Output the specular power. + vec4 specularIntensity = pow( rDotL, vec4(specularPower) ) * atten; + + // Apply the per-light specular attenuation. + vec4 specular = vec4(0,0,0,1); + for ( i = 0; i < 4; i++ ) + specular += vec4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 ); + + // Add the final specular intensity values together + // using a single dot product operation then get the + // final specular lighting color. + outSpecular = specularColor * specular; +} + + +// This value is used in AL as a constant power to raise specular values +// to, before storing them into the light info buffer. The per-material +// specular value is then computer by using the integer identity of +// exponentiation: +// +// (a^m)^n = a^(m*n) +// +// or +// +// (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular) +// +#define AL_ConstantSpecularPower 12.0f + +/// The specular calculation used in Advanced Lighting. +/// +/// @param toLight Normalized vector representing direction from the pixel +/// being lit, to the light source, in world space. +/// +/// @param normal Normalized surface normal. +/// +/// @param toEye The normalized vector representing direction from the pixel +/// being lit to the camera. +/// +float AL_CalcSpecular( vec3 toLight, vec3 normal, vec3 toEye ) +{ + // (R.V)^c + float specVal = dot( normalize( -reflect( toLight, normal ) ), toEye ); + + // Return the specular factor. + return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower ); +} + +/// The output for Deferred Lighting +/// +/// @param toLight Normalized vector representing direction from the pixel +/// being lit, to the light source, in world space. +/// +/// @param normal Normalized surface normal. +/// +/// @param toEye The normalized vector representing direction from the pixel +/// being lit to the camera. +/// +vec4 AL_DeferredOutput( + vec3 lightColor, + vec3 diffuseColor, + vec4 matInfo, + vec4 ambient, + float specular, + float shadowAttenuation) +{ + vec3 specularColor = vec3(specular); + bool metalness = getFlag(matInfo.r, 3); + if ( metalness ) + { + specularColor = 0.04 * (1 - specular) + diffuseColor * specular; + } + + //specular = color * map * spec^gloss + float specularOut = (specularColor * matInfo.b * min(pow(max(specular,1.0f), max((matInfo.a / AL_ConstantSpecularPower),1.0f)),matInfo.a)).r; + + lightColor *= vec3(shadowAttenuation); + lightColor += ambient.rgb; + return vec4(lightColor.rgb, specularOut); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeP.glsl new file mode 100644 index 000000000..e33c9bd97 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeP.glsl @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.glsl" +#include "hlslCompat.glsl" + +in vec4 offscreenPos; +in vec4 backbufferPos; + +#define IN_offscreenPos offscreenPos +#define IN_backbufferPos backbufferPos + +uniform sampler2D colorSource; +uniform vec4 offscreenTargetParams; + +#ifdef TORQUE_LINEAR_DEPTH +#define REJECT_EDGES +uniform sampler2D edgeSource; +uniform vec4 edgeTargetParams; +#endif + +out vec4 OUT_col; + +void main() +{ + // Off-screen particle source screenspace position in XY + // Back-buffer screenspace position in ZW + vec4 ssPos = vec4(offscreenPos.xy / offscreenPos.w, backbufferPos.xy / backbufferPos.w); + + vec4 uvScene = ( ssPos + 1.0 ) / 2.0; + uvScene.yw = 1.0 - uvScene.yw; + uvScene.xy = viewportCoordToRenderTarget(uvScene.xy, offscreenTargetParams); + +#ifdef REJECT_EDGES + // Cut out particles along the edges, this will create the stencil mask + uvScene.zw = viewportCoordToRenderTarget(uvScene.zw, edgeTargetParams); + float edge = texture( edgeSource, uvScene.zw ).r; + clip( -edge ); +#endif + + // Sample offscreen target and return + OUT_col = texture( colorSource, uvScene.xy ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeV.glsl new file mode 100644 index 000000000..8c8f840d1 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/particleCompositeV.glsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +in vec2 vTexCoord0; +#define uvCoord vTexCoord0 + +out vec4 offscreenPos; +out vec4 backbufferPos; + +#define OUT_hpos gl_Position +#define OUT_offscreenPos offscreenPos +#define OUT_backbufferPos backbufferPos + +uniform vec4 screenRect; // point, extent + +void main() +{ + OUT_hpos = vec4(uvCoord.xy, 1.0, 1.0); + OUT_hpos.xy *= screenRect.zw; + OUT_hpos.xy += screenRect.xy; + + OUT_backbufferPos = OUT_hpos; + OUT_offscreenPos = OUT_hpos; + + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/gl/particlesP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/particlesP.glsl new file mode 100644 index 000000000..813e31a1d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/particlesP.glsl @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.glsl" +#include "hlslCompat.glsl" + +// With advanced lighting we get soft particles. +#ifdef TORQUE_LINEAR_DEPTH + #define SOFTPARTICLES +#endif + +#ifdef SOFTPARTICLES + + #include "shadergen:/autogenConditioners.h" + + uniform float oneOverSoftness; + uniform float oneOverFar; + uniform sampler2D prepassTex; + //uniform vec3 vEye; + uniform vec4 prePassTargetParams; +#endif + +#define CLIP_Z // TODO: Make this a proper macro + +in vec4 color; +in vec2 uv0; +in vec4 pos; + +#define IN_color color +#define IN_uv0 uv0 +#define IN_pos pos + +uniform sampler2D diffuseMap; + +uniform sampler2D paraboloidLightMap; + +vec4 lmSample( vec3 nrm ) +{ + bool calcBack = (nrm.z < 0.0); + if ( calcBack ) + nrm.z = nrm.z * -1.0; + + vec2 lmCoord; + lmCoord.x = (nrm.x / (2*(1 + nrm.z))) + 0.5; + lmCoord.y = 1-((nrm.y / (2*(1 + nrm.z))) + 0.5); + + + // If this is the back, offset in the atlas + if ( calcBack ) + lmCoord.x += 1.0; + + // Atlasing front and back maps, so scale + lmCoord.x *= 0.5; + + return texture(paraboloidLightMap, lmCoord); +} + + +uniform float alphaFactor; +uniform float alphaScale; + +out vec4 OUT_col; + +void main() +{ + float softBlend = 1; + + #ifdef SOFTPARTICLES + vec2 tc = IN_pos.xy * vec2(1.0, -1.0) / IN_pos.w; + tc = viewportCoordToRenderTarget(saturate( ( tc + 1.0 ) * 0.5 ), prePassTargetParams); + + float sceneDepth = prepassUncondition( prepassTex, tc ).w; + float depth = IN_pos.w * oneOverFar; + float diff = sceneDepth - depth; + #ifdef CLIP_Z + // If drawing offscreen, this acts as the depth test, since we don't line up with the z-buffer + // When drawing high-res, though, we want to be able to take advantage of hi-z + // so this is #ifdef'd out + //clip(diff); + #endif + softBlend = saturate( diff * oneOverSoftness ); + #endif + + vec4 diffuse = texture( diffuseMap, IN_uv0 ); + + //OUT_col = vec4( lmSample(vec3(0, 0, -1)).rgb, IN_color.a * diffuse.a * softBlend * alphaScale); + + // Scale output color by the alpha factor (turn LerpAlpha into pre-multiplied alpha) + vec3 colorScale = ( alphaFactor < 0.0 ? IN_color.rgb * diffuse.rgb : vec3( alphaFactor > 0.0 ? IN_color.a * diffuse.a * alphaFactor * softBlend : softBlend ) ); + + OUT_col = hdrEncode( vec4( IN_color.rgb * diffuse.rgb * colorScale, + IN_color.a * diffuse.a * softBlend * alphaScale ) ); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/gl/particlesV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/particlesV.glsl new file mode 100644 index 000000000..3d75a6fb6 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/particlesV.glsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +#define In_pos vPosition +#define In_color vColor +#define In_uv0 vTexCoord0 + +out vec4 color; +out vec2 uv0; +out vec4 pos; + +#define OUT_hpos gl_Position +#define OUT_color color +#define OUT_uv0 uv0 +#define OUT_pos pos + +uniform mat4 modelViewProj; +uniform mat4 fsModelViewProj; + +void main() +{ + OUT_hpos = tMul( modelViewProj, In_pos ); + OUT_pos = tMul( fsModelViewProj, In_pos ); + OUT_color = In_color; + OUT_uv0 = In_uv0; + + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpP.glsl new file mode 100644 index 000000000..db4250487 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpP.glsl @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +uniform sampler2D diffuseMap, refractMap, bumpMap; +uniform vec4 shadeColor; + +in vec2 TEX0; +in vec4 TEX1; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Fade edges of axis for texcoord passed in +//----------------------------------------------------------------------------- +float fadeAxis( float val ) +{ + // Fades from 1.0 to 0.0 when less than 0.1 + float fadeLow = clamp( val * 10.0, 0.0, 1.0 ); + + // Fades from 1.0 to 0.0 when greater than 0.9 + float fadeHigh = 1.0 - clamp( (val - 0.9) * 10.0, 0.0, 1.0 ); + + return fadeLow * fadeHigh; +} + + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec3 bumpNorm = texture( bumpMap, TEX0 ).rgb * 2.0 - 1.0; + vec2 offset = vec2( bumpNorm.x, bumpNorm.y ); + vec4 texIndex = TEX1; + + // The fadeVal is used to "fade" the distortion at the edges of the screen. + // This is done so it won't sample the reflection texture out-of-bounds and create artifacts + // Note - this can be done more efficiently with a texture lookup + float fadeVal = fadeAxis( texIndex.x / texIndex.w ) * fadeAxis( texIndex.y / texIndex.w ); + + const float distortion = 0.2; + texIndex.xy += offset * distortion * fadeVal; + + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, texIndex ); + + OUT_col = diffuseColor + reflectColor * diffuseColor.a; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpV.glsl new file mode 100644 index 000000000..90bcd27d8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectBumpV.glsl @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4 modelview; + +out vec2 TEX0; +out vec4 TEX1; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + mat4 texGenTest = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, -0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0); + + gl_Position = modelview * vPosition; + + TEX0 = vTexCoord0.st; + + TEX1 = texGenTest * gl_Position; + TEX1.y = -TEX1.y; + gl_Position.y *= -1; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/planarReflectP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectP.glsl new file mode 100644 index 000000000..384c16188 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectP.glsl @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +uniform sampler2D diffuseMap, refractMap; +uniform vec4 shadeColor; + +in vec2 TEX0; +in vec4 TEX1; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, TEX1 ); + + OUT_col = diffuseColor + reflectColor * diffuseColor.a; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/planarReflectV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectV.glsl new file mode 100644 index 000000000..ba2484f66 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/planarReflectV.glsl @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4 modelview; + +out vec2 TEX0; +out vec4 TEX1; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + mat4 texGenTest = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, -0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0); + + gl_Position = modelview * vPosition; + + TEX0 = vTexCoord0; + + TEX1 = texGenTest * gl_Position; + TEX1.y = -TEX1.y; + +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/precipP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/precipP.glsl new file mode 100644 index 000000000..102d0b0aa --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/precipP.glsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +uniform sampler2D diffuseMap; + +in vec4 color; +in vec2 texCoord; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + OUT_col = texture(diffuseMap, texCoord) * color; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/precipV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/precipV.glsl new file mode 100644 index 000000000..29f921630 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/precipV.glsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4 modelview; +uniform vec3 cameraPos, ambient; +uniform vec2 fadeStartEnd; + +out vec4 color; +out vec2 texCoord; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + gl_Position = modelview * vPosition; + texCoord = vTexCoord0.st; + color = vec4( ambient.r, ambient.g, ambient.b, 1.0 ); + + // Do we need to do a distance fade? + if ( fadeStartEnd.x < fadeStartEnd.y ) + { + + float distance = length( cameraPos - vPosition.xyz ); + color.a = abs( clamp( ( distance - fadeStartEnd.x ) / ( fadeStartEnd.y - fadeStartEnd.x ), 0.0, 1.0 ) - 1.0 ); + } + gl_Position.y *= -1; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowP.glsl new file mode 100644 index 000000000..9b0ff0d0b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowP.glsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +in vec2 texCoord; +in vec4 color; +in float fade; + +out vec4 OUT_col; + +uniform sampler2D inputTex; +uniform vec4 ambient; + + +void main() +{ + float shadow = texture( inputTex, texCoord ).a * color.a; + OUT_col = ( ambient * shadow ) + ( 1 - shadow ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowV.glsl new file mode 100644 index 000000000..c8b6d2a92 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/projectedShadowV.glsl @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; + +out vec2 texCoord; +out vec4 color; +out float fade; + +uniform mat4 modelview; +uniform float shadowLength; +uniform vec3 shadowCasterPosition; + +void main() +{ + gl_Position = modelview * vec4(vPosition.xyz, 1.0); + + color = vColor; + texCoord = vTexCoord0.st; + + float fromCasterDist = length(vPosition.xyz - shadowCasterPosition) - shadowLength; + fade = 1.0 - clamp( fromCasterDist / shadowLength , 0.0, 1.0 ); + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyP.glsl new file mode 100644 index 000000000..6d4e3ea75 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyP.glsl @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.glsl" +#include "hlslCompat.glsl" + + +// Conn +in vec4 rayleighColor; +#define IN_rayleighColor rayleighColor +in vec4 mieColor; +#define IN_mieColor mieColor +in vec3 v3Direction; +#define IN_v3Direction v3Direction +in vec3 pos; +#define IN_pos pos + +uniform samplerCube nightSky ; +uniform vec4 nightColor; +uniform vec2 nightInterpAndExposure; +uniform float useCubemap; +uniform vec3 lightDir; +uniform vec3 sunDir; + +out vec4 OUT_col; + +void main() +{ + + float fCos = dot( lightDir, IN_v3Direction ) / length(IN_v3Direction); + float fCos2 = fCos*fCos; + + float g = -0.991; + float g2 = -0.991 * -0.991; + + float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); + + vec4 color = IN_rayleighColor + fMiePhase * IN_mieColor; + color.a = color.b; + + vec4 nightSkyColor = texture(nightSky, -v3Direction); + nightSkyColor = mix(nightColor, nightSkyColor, useCubemap); + + float fac = dot( normalize( pos ), sunDir ); + fac = max( nightInterpAndExposure.y, pow( clamp( fac, 0.0, 1.0 ), 2 ) ); + OUT_col = mix( color, nightSkyColor, nightInterpAndExposure.y ); + + OUT_col.a = 1; + + OUT_col = clamp(OUT_col, 0.0, 1.0); + + OUT_col = hdrEncode( OUT_col ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyV.glsl b/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyV.glsl new file mode 100644 index 000000000..5780d2df9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/scatterSkyV.glsl @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslCompat.glsl" + +// The scale equation calculated by Vernier's Graphical Analysis +float vernierScale(float fCos) +{ + float x = 1.0 - fCos; + float x5 = x * 5.25; + float x5p6 = (-6.80 + x5); + float xnew = (3.83 + x * x5p6); + float xfinal = (0.459 + x * xnew); + float xfinal2 = -0.00287 + x * xfinal; + float outx = exp( xfinal2 ); + return 0.25 * outx; +} + +in vec4 vPosition; + +// This is the shader input vertex structure. +#define IN_position vPosition + +// This is the shader output data. +out vec4 rayleighColor; +#define OUT_rayleighColor rayleighColor +out vec4 mieColor; +#define OUT_mieColor mieColor +out vec3 v3Direction; +#define OUT_v3Direction v3Direction +out vec3 pos; +#define OUT_pos pos + +uniform mat4 modelView; +uniform vec4 misc; +uniform vec4 sphereRadii; +uniform vec4 scatteringCoeffs; +uniform vec3 camPos; +uniform vec3 lightDir; +uniform vec4 invWaveLength; +uniform vec4 colorize; + +vec3 desaturate(const vec3 color, const float desaturation) +{ + const vec3 gray_conv = vec3 (0.30, 0.59, 0.11); + return mix(color, vec3(dot(gray_conv , color)), desaturation); +} + +void main() +{ + // Pull some variables out: + float camHeight = misc.x; + float camHeightSqr = misc.y; + + float scale = misc.z; + float scaleOverScaleDepth = misc.w; + + float outerRadius = sphereRadii.x; + float outerRadiusSqr = sphereRadii.y; + + float innerRadius = sphereRadii.z; + float innerRadiusSqr = sphereRadii.w; + + float rayleighBrightness = scatteringCoeffs.x; // Kr * ESun + float rayleigh4PI = scatteringCoeffs.y; // Kr * 4 * PI + + float mieBrightness = scatteringCoeffs.z; // Km * ESun + float mie4PI = scatteringCoeffs.w; // Km * 4 * PI + + // Get the ray from the camera to the vertex, + // and its length (which is the far point of the ray + // passing through the atmosphere). + vec3 v3Pos = vec3(IN_position / 6378000.0);// / outerRadius; + vec3 newCamPos = vec3( 0, 0, camHeight ); + v3Pos.z += innerRadius; + vec3 v3Ray = v3Pos.xyz - newCamPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the ray's starting position, + // then calculate its scattering offset. + vec3 v3Start = newCamPos; + float fHeight = length(v3Start); + float fDepth = exp(scaleOverScaleDepth * (innerRadius - camHeight)); + float fStartAngle = dot(v3Ray, v3Start) / fHeight; + + float fStartOffset = fDepth * vernierScale( fStartAngle ); + + // Initialize the scattering loop variables. + float fSampleLength = fFar / 2.0; + float fScaledLength = fSampleLength * scale; + vec3 v3SampleRay = v3Ray * fSampleLength; + vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); + for(int i=0; i<2; i++) + { + float fHeight = length(v3SamplePoint); + float fDepth = exp(scaleOverScaleDepth * (innerRadius - fHeight)); + float fLightAngle = dot(lightDir, v3SamplePoint) / fHeight; + float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; + + float vscale3 = vernierScale( fCameraAngle ); + float vscale2 = vernierScale( fLightAngle ); + + float fScatter = (fStartOffset + fDepth*(vscale2 - vscale3)); + vec3 v3Attenuate = exp(-fScatter * (invWaveLength.xyz * rayleigh4PI + mie4PI)); + v3FrontColor += v3Attenuate * (fDepth * fScaledLength); + v3SamplePoint += v3SampleRay; + } + + // Finally, scale the Mie and Rayleigh colors + // and set up the varying variables for the pixel shader. + gl_Position = modelView * IN_position; + OUT_mieColor.rgb = v3FrontColor * mieBrightness; + OUT_mieColor.a = 1.0; + OUT_rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); + OUT_rayleighColor.a = 1.0; + OUT_v3Direction = newCamPos - v3Pos.xyz; + OUT_pos = IN_position.xyz; + +#ifdef USE_COLORIZE + + OUT_rayleighColor.rgb = desaturate(OUT_rayleighColor.rgb, 1) * colorize.a; + + OUT_rayleighColor.r *= colorize.r; + OUT_rayleighColor.g *= colorize.g; + OUT_rayleighColor.b *= colorize.b; + +#endif + + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/gl/torque.glsl b/Templates/BaseGame/game/data/shaders/common/gl/torque.glsl new file mode 100644 index 000000000..6e369bd5e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/torque.glsl @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_GLSL_ +#define _TORQUE_GLSL_ + + +float M_HALFPI_F = 1.57079632679489661923; +float M_PI_F = 3.14159265358979323846; +float M_2PI_F = 6.28318530717958647692; + +/// Calculate fog based on a start and end positions in worldSpace. +float computeSceneFog( vec3 startPos, + vec3 endPos, + float fogDensity, + float fogDensityOffset, + float fogHeightFalloff ) +{ + float f = length( startPos - endPos ) - fogDensityOffset; + float h = 1.0 - ( endPos.z * fogHeightFalloff ); + return exp( -fogDensity * f * h ); +} + + +/// Calculate fog based on a start and end position and a height. +/// Positions do not need to be in worldSpace but height does. +float computeSceneFog( vec3 startPos, + vec3 endPos, + float height, + float fogDensity, + float fogDensityOffset, + float fogHeightFalloff ) +{ + float f = length( startPos - endPos ) - fogDensityOffset; + float h = 1.0 - ( height * fogHeightFalloff ); + return exp( -fogDensity * f * h ); +} + + +/// Calculate fog based on a distance, height is not used. +float computeSceneFog( float dist, float fogDensity, float fogDensityOffset ) +{ + float f = dist - fogDensityOffset; + return exp( -fogDensity * f ); +} + + +/// Convert a vec4 uv in viewport space to render target space. +vec2 viewportCoordToRenderTarget( vec4 inCoord, vec4 rtParams ) +{ + vec2 outCoord = inCoord.xy / inCoord.w; + outCoord = ( outCoord * rtParams.zw ) + rtParams.xy; + return outCoord; +} + + +/// Convert a vec2 uv in viewport space to render target space. +vec2 viewportCoordToRenderTarget( vec2 inCoord, vec4 rtParams ) +{ + vec2 outCoord = ( inCoord * rtParams.zw ) + rtParams.xy; + return outCoord; +} + + +/// Convert a vec4 quaternion into a 3x3 matrix. +mat3x3 quatToMat( vec4 quat ) +{ + float xs = quat.x * 2.0; + float ys = quat.y * 2.0; + float zs = quat.z * 2.0; + + float wx = quat.w * xs; + float wy = quat.w * ys; + float wz = quat.w * zs; + + float xx = quat.x * xs; + float xy = quat.x * ys; + float xz = quat.x * zs; + + float yy = quat.y * ys; + float yz = quat.y * zs; + float zz = quat.z * zs; + + mat3x3 mat; + + mat[0][0] = 1.0 - (yy + zz); + mat[1][0] = xy - wz; + mat[2][0] = xz + wy; + + mat[0][1] = xy + wz; + mat[1][1] = 1.0 - (xx + zz); + mat[2][1] = yz - wx; + + mat[0][2] = xz - wy; + mat[1][2] = yz + wx; + mat[2][2] = 1.0 - (xx + yy); + + return mat; +} + + +/// The number of additional substeps we take when refining +/// the results of the offset parallax mapping function below. +/// +/// You should turn down the number of steps if your needing +/// more performance out of your parallax surfaces. Increasing +/// the number doesn't yeild much better results and is rarely +/// worth the additional cost. +/// +#define PARALLAX_REFINE_STEPS 3 + +/// Performs fast parallax offset mapping using +/// multiple refinement steps. +/// +/// @param texMap The texture map whos alpha channel we sample the parallax depth. +/// @param texCoord The incoming texture coordinate for sampling the parallax depth. +/// @param negViewTS The negative view vector in tangent space. +/// @param depthScale The parallax factor used to scale the depth result. +/// +vec2 parallaxOffset( sampler2D texMap, vec2 texCoord, vec3 negViewTS, float depthScale ) +{ + float depth = texture( texMap, texCoord ).a/(PARALLAX_REFINE_STEPS*2); + vec2 offset = negViewTS.xy * vec2( depth * depthScale )/vec2(PARALLAX_REFINE_STEPS*2); + + for ( int i=0; i < PARALLAX_REFINE_STEPS; i++ ) + { + depth = ( depth + texture( texMap, texCoord + offset ).a )/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * vec2( depth * depthScale )/vec2(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + +/// Same as parallaxOffset but for dxtnm where depth is stored in the red channel instead of the alpha +vec2 parallaxOffsetDxtnm(sampler2D texMap, vec2 texCoord, vec3 negViewTS, float depthScale) +{ + float depth = texture(texMap, texCoord).r/(PARALLAX_REFINE_STEPS*2); + vec2 offset = negViewTS.xy * vec2(depth * depthScale)/vec2(PARALLAX_REFINE_STEPS*2); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + texture(texMap, texCoord + offset).r)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * vec2(depth * depthScale)/vec2(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + + +/// The maximum value for 16bit per component integer HDR encoding. +const float HDR_RGB16_MAX = 100.0; +/// The maximum value for 10bit per component integer HDR encoding. +const float HDR_RGB10_MAX = 4.0; + +/// Encodes an HDR color for storage into a target. +vec3 hdrEncode( vec3 _sample ) +{ + #if defined( TORQUE_HDR_RGB16 ) + + return _sample / HDR_RGB16_MAX; + + #elif defined( TORQUE_HDR_RGB10 ) + + return _sample / HDR_RGB10_MAX; + + #else + + // No encoding. + return _sample; + + #endif +} + +/// Encodes an HDR color for storage into a target. +vec4 hdrEncode( vec4 _sample ) +{ + return vec4( hdrEncode( _sample.rgb ), _sample.a ); +} + +/// Decodes an HDR color from a target. +vec3 hdrDecode( vec3 _sample ) +{ + #if defined( TORQUE_HDR_RGB16 ) + + return _sample * HDR_RGB16_MAX; + + #elif defined( TORQUE_HDR_RGB10 ) + + return _sample * HDR_RGB10_MAX; + + #else + + // No encoding. + return _sample; + + #endif +} + +/// Decodes an HDR color from a target. +vec4 hdrDecode( vec4 _sample ) +{ + return vec4( hdrDecode( _sample.rgb ), _sample.a ); +} + +/// Returns the luminance for an HDR pixel. +float hdrLuminance( vec3 _sample ) +{ + // There are quite a few different ways to + // calculate luminance from an rgb value. + // + // If you want to use a different technique + // then plug it in here. + // + + //////////////////////////////////////////////////////////////////////////// + // + // Max component luminance. + // + //float lum = max( _sample.r, max( _sample.g, _sample.b ) ); + + //////////////////////////////////////////////////////////////////////////// + // The perceptual relative luminance. + // + // See http://en.wikipedia.org/wiki/Luminance_(relative) + // + const vec3 RELATIVE_LUMINANCE = vec3( 0.2126, 0.7152, 0.0722 ); + float lum = dot( _sample, RELATIVE_LUMINANCE ); + + //////////////////////////////////////////////////////////////////////////// + // + // The average component luminance. + // + //const vec3 AVERAGE_LUMINANCE = vec3( 0.3333, 0.3333, 0.3333 ); + //float lum = dot( _sample, AVERAGE_LUMINANCE ); + + return lum; +} + +#ifdef TORQUE_PIXEL_SHADER +/// Called from the visibility feature to do screen +/// door transparency for fading of objects. +void fizzle(vec2 vpos, float visibility) +{ + // NOTE: The magic values below are what give us + // the nice even pattern during the fizzle. + // + // These values can be changed to get different + // patterns... some better than others. + // + // Horizontal Blinds - { vpos.x, 0.916, vpos.y, 0 } + // Vertical Lines - { vpos.x, 12.9898, vpos.y, 78.233 } + // + // I'm sure there are many more patterns here to + // discover for different effects. + + mat2x2 m = mat2x2( vpos.x, vpos.y, 0.916, 0.350 ); + if( (visibility - fract( determinant( m ) )) < 0 ) //if(a < 0) discard; + discard; +} +#endif //TORQUE_PIXEL_SHADER + +/// Basic assert macro. If the condition fails, then the shader will output color. +/// @param condition This should be a bvec[2-4]. If any items is false, condition is considered to fail. +/// @param color The color that should be outputted if the condition fails. +/// @note This macro will only work in the void main() method of a pixel shader. +#define assert(condition, color) { if(!any(condition)) { OUT_col = color; return; } } + +// Deferred Shading: Material Info Flag Check +bool getFlag(float flags, float num) +{ + float process = round(flags * 255); + float squareNum = pow(2.0, num); + return (mod(process, pow(2.0, squareNum)) >= squareNum); +} + +// #define TORQUE_STOCK_GAMMA +#ifdef TORQUE_STOCK_GAMMA +// Sample in linear space. Decodes gamma. +vec4 toLinear(vec4 tex) +{ + return tex; +} +// Encodes gamma. +vec4 toGamma(vec4 tex) +{ + return tex; +} +vec3 toLinear(vec3 tex) +{ + return tex; +} +// Encodes gamma. +vec3 toGamma(vec3 tex) +{ + return tex; +} +#else +// Sample in linear space. Decodes gamma. +vec4 toLinear(vec4 tex) +{ + return vec4(pow(abs(tex.rgb), vec3(2.2)), tex.a); +} +// Encodes gamma. +vec4 toGamma(vec4 tex) +{ + return vec4(pow(abs(tex.rgb), vec3(1.0/2.2)), tex.a); +} +// Sample in linear space. Decodes gamma. +vec3 toLinear(vec3 tex) +{ + return pow(abs(tex), vec3(2.2)); +} +// Encodes gamma. +vec3 toGamma(vec3 tex) +{ + return pow(abs(tex), vec3(1.0/2.2)); +} +#endif // + +#endif // _TORQUE_GLSL_ diff --git a/Templates/BaseGame/game/data/shaders/common/gl/wavesP.glsl b/Templates/BaseGame/game/data/shaders/common/gl/wavesP.glsl new file mode 100644 index 000000000..06c8a1a28 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/wavesP.glsl @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +uniform sampler2D diffMap; +uniform sampler2D bumpMap; +uniform samplerCube cubeMap; +uniform vec4 specularColor; +uniform float specularPower; +uniform vec4 ambient; +uniform float accumTime; + +in vec2 TEX0; +in vec4 outLightVec; +in vec3 outPos; +in vec3 outEyePos; + +out vec4 OUT_col; + +void main() +{ + vec2 texOffset; + float sinOffset1 = sin( accumTime * 1.5 + TEX0.y * 6.28319 * 3.0 ) * 0.03; + float sinOffset2 = sin( accumTime * 3.0 + TEX0.y * 6.28319 ) * 0.04; + + texOffset.x = TEX0.x + sinOffset1 + sinOffset2; + texOffset.y = TEX0.y + cos( accumTime * 3.0 + TEX0.x * 6.28319 * 2.0 ) * 0.05; + + vec4 bumpNorm = texture(bumpMap, texOffset) * 2.0 - 1.0; + vec4 diffuse = texture(diffMap, texOffset); + + OUT_col = diffuse * (clamp(dot(outLightVec.xyz, bumpNorm.xyz), 0.0, 1.0) + ambient); + + vec3 eyeVec = normalize(outEyePos - outPos); + vec3 halfAng = normalize(eyeVec + outLightVec.xyz); + float specular = clamp(dot(bumpNorm.xyz, halfAng), 0.0, 1.0) * outLightVec.w; + specular = pow(specular, specularPower); + OUT_col += specularColor * specular; +} diff --git a/Templates/BaseGame/game/data/shaders/common/gl/wind.glsl b/Templates/BaseGame/game/data/shaders/common/gl/wind.glsl new file mode 100644 index 000000000..0ddb492b9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/gl/wind.glsl @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// +// A tip of the hat.... +// +// The following wind effects were derived from the GPU Gems +// 3 chapter "Vegetation Procedural Animation and Shading in Crysis" +// by Tiago Sousa of Crytek. +// + +vec4 smoothCurve( vec4 x ) +{ + return x * x * ( 3.0 - 2.0 * x ); +} + +vec4 triangleWave( vec4 x ) +{ + return abs( fract( x + 0.5 ) * 2.0 - 1.0 ); +} + +vec4 smoothTriangleWave( vec4 x ) +{ + return smoothCurve( triangleWave( x ) ); +} + +vec3 windTrunkBending( vec3 vPos, vec2 vWind, float fBendFactor ) +{ + // Smooth the bending factor and increase + // the near by height limit. + fBendFactor += 1.0; + fBendFactor *= fBendFactor; + fBendFactor = fBendFactor * fBendFactor - fBendFactor; + + // Displace the vert. + vec3 vNewPos = vPos; + vNewPos.xy += vWind * fBendFactor; + + // Limit the length which makes the bend more + // spherical and prevents stretching. + float fLength = length( vPos ); + vPos = normalize( vNewPos ) * fLength; + + return vPos; +} + +vec3 windBranchBending( vec3 vPos, + vec3 vNormal, + + float fTime, + float fWindSpeed, + + float fBranchPhase, + float fBranchAmp, + float fBranchAtten, + + float fDetailPhase, + float fDetailAmp, + float fDetailFreq, + + float fEdgeAtten ) +{ + float fVertPhase = dot( vPos, vec3( fDetailPhase + fBranchPhase ) ); + + vec2 vWavesIn = fTime + vec2( fVertPhase, fBranchPhase ); + + vec4 vWaves = ( fract( vWavesIn.xxyy * + vec4( 1.975, 0.793, 0.375, 0.193 ) ) * + 2.0 - 1.0 ) * fWindSpeed * fDetailFreq; + + vWaves = smoothTriangleWave( vWaves ); + + vec2 vWavesSum = vWaves.xz + vWaves.yw; + + // We want the branches to bend both up and down. + vWavesSum.y = 1.0 - ( vWavesSum.y * 2.0 ); + + vPos += vWavesSum.xxy * vec3( fEdgeAtten * fDetailAmp * vNormal.xy, + fBranchAtten * fBranchAmp ); + + return vPos; +} diff --git a/Templates/BaseGame/game/data/shaders/common/guiMaterialV.hlsl b/Templates/BaseGame/game/data/shaders/common/guiMaterialV.hlsl new file mode 100644 index 000000000..5d725338f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/guiMaterialV.hlsl @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "hlslStructs.hlsl" +#include "shaderModel.hlsl" + +struct MaterialDecoratorConnectV +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; +}; + +uniform float4x4 modelview : register(C0); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +MaterialDecoratorConnectV main( VertexIn_PCT IN ) +{ + MaterialDecoratorConnectV OUT; + + OUT.hpos = mul(modelview, float4(IN.pos,1.0)); + OUT.uv0 = IN.uv0; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/hlslStructs.h b/Templates/BaseGame/game/data/shaders/common/hlslStructs.h new file mode 100644 index 000000000..6a57e4db7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/hlslStructs.h @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// The purpose of this file is to get all of our HLSL structures into one place. +// Please use the structures here instead of redefining input and output structures +// in each shader file. If structures are added, please adhere to the naming convention. + +//------------------------------------------------------------------------------ +// Vertex Input Structures +// +// These structures map to FVFs/Vertex Declarations in Torque. See gfxStructs.h +//------------------------------------------------------------------------------ + +// Notes +// +// Position should be specified as a float4. Right now our vertex structures in +// the engine output float3s for position. This does NOT mean that the POSITION +// binding should be float3, because it will assign 0 to the w coordinate, which +// results in the vertex not getting translated when it is transformed. + +struct VertexIn_P +{ + float4 pos : POSITION; +}; + +struct VertexIn_PT +{ + float4 pos : POSITION; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PTTT +{ + float4 pos : POSITION; + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; +}; + +struct VertexIn_PC +{ + float4 pos : POSITION; + float4 color : DIFFUSE; +}; + +struct VertexIn_PNC +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float4 color : DIFFUSE; +}; + +struct VertexIn_PCT +{ + float4 pos : POSITION; + float4 color : DIFFUSE; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PN +{ + float4 pos : POSITION; + float3 normal : NORMAL; +}; + +struct VertexIn_PNT +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNTT +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float3 tangent : TANGENT; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNCT +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float4 color : DIFFUSE; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNTTTB +{ + float4 pos : POSITION; + float3 normal : NORMAL; + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float3 T : TEXCOORD2; + float3 B : TEXCOORD3; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/hlslStructs.hlsl b/Templates/BaseGame/game/data/shaders/common/hlslStructs.hlsl new file mode 100644 index 000000000..ce0ca305c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/hlslStructs.hlsl @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// The purpose of this file is to get all of our HLSL structures into one place. +// Please use the structures here instead of redefining input and output structures +// in each shader file. If structures are added, please adhere to the naming convention. + +//------------------------------------------------------------------------------ +// Vertex Input Structures +// +// These structures map to FVFs/Vertex Declarations in Torque. See gfxStructs.h +//------------------------------------------------------------------------------ + +// Notes +// +// Position should be specified as a float3 as our vertex structures in +// the engine output float3s for position. + +struct VertexIn_P +{ + float3 pos : POSITION; +}; + +struct VertexIn_PT +{ + float3 pos : POSITION; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PTTT +{ + float3 pos : POSITION; + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; +}; + +struct VertexIn_PC +{ + float3 pos : POSITION; + float4 color : DIFFUSE; +}; + +struct VertexIn_PNC +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float4 color : DIFFUSE; +}; + +struct VertexIn_PCT +{ + float3 pos : POSITION; + float4 color : DIFFUSE; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PN +{ + float3 pos : POSITION; + float3 normal : NORMAL; +}; + +struct VertexIn_PNT +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNTT +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float3 tangent : TANGENT; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNCT +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float4 color : DIFFUSE; + float2 uv0 : TEXCOORD0; +}; + +struct VertexIn_PNTTTB +{ + float3 pos : POSITION; + float3 normal : NORMAL; + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float3 T : TEXCOORD2; + float3 B : TEXCOORD3; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/imposter.hlsl b/Templates/BaseGame/game/data/shaders/common/imposter.hlsl new file mode 100644 index 000000000..bc700ba03 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/imposter.hlsl @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.hlsl" + + +static float sCornerRight[4] = { -1, 1, 1, -1 }; +static float sCornerUp[4] = { -1, -1, 1, 1 }; +static float2 sUVCornerExtent[4] = +{ + float2( 0, 1 ), + float2( 1, 1 ), + float2( 1, 0 ), + float2( 0, 0 ) +}; + +#define IMPOSTER_MAX_UVS 64 + + +void imposter_v( + // These parameters usually come from the vertex. + float3 center, + int corner, + float halfSize, + float3 imposterUp, + float3 imposterRight, + + // These are from the imposter shader constant. + int numEquatorSteps, + int numPolarSteps, + float polarAngle, + bool includePoles, + + // Other shader constants. + float3 camPos, + float4 uvs[IMPOSTER_MAX_UVS], + + // The outputs of this function. + out float3 outWsPosition, + out float2 outTexCoord, + out float3x3 outWorldToTangent + ) +{ + // TODO: This could all be calculated on the CPU. + float equatorStepSize = M_2PI_F / numEquatorSteps; + float equatorHalfStep = ( equatorStepSize / 2.0 ) - 0.0001; + float polarStepSize = M_PI_F / numPolarSteps; + float polarHalfStep = ( polarStepSize / 2.0 ) - 0.0001; + + // The vector between the camera and the billboard. + float3 lookVec = normalize( camPos - center ); + + // Generate the camera up and right vectors from + // the object transform and camera forward. + float3 camUp = imposterUp; + float3 camRight = normalize( cross( -lookVec, camUp ) ); + + // The billboarding is based on the camera directions. + float3 rightVec = camRight * sCornerRight[corner]; + float3 upVec = camUp * sCornerUp[corner]; + + float lookPitch = acos( dot( imposterUp, lookVec ) ); + + // First check to see if we need to render the top billboard. + int index; + /* + if ( includePoles && ( lookPitch < polarAngle || lookPitch > sPi - polarAngle ) ) + { + index = numEquatorSteps * 3; + + // When we render the top/bottom billboard we always use + // a fixed vector that matches the rotation of the object. + rightVec = float3( 1, 0, 0 ) * sCornerRight[corner]; + upVec = float3( 0, 1, 0 ) * sCornerUp[corner]; + + if ( lookPitch > sPi - polarAngle ) + { + upVec = -upVec; + index++; + } + } + else + */ + { + // Calculate the rotation around the z axis then add the + // equator half step. This gets the images to switch a + // half step before the captured angle is met. + float lookAzimuth = atan2( lookVec.y, lookVec.x ); + float azimuth = atan2( imposterRight.y, imposterRight.x ); + float rotZ = ( lookAzimuth - azimuth ) + equatorHalfStep; + + // The y rotation is calculated from the look vector and + // the object up vector. + float rotY = lookPitch - polarHalfStep; + + // TODO: How can we do this without conditionals? + // Normalize the result to 0 to 2PI. + if ( rotZ < 0 ) + rotZ += M_2PI_F; + if ( rotZ > M_2PI_F ) + rotZ -= M_2PI_F; + if ( rotY < 0 ) + rotY += M_2PI_F; + if ( rotY > M_PI_F ) // Not M_2PI_F? + rotY -= M_2PI_F; + + float polarIdx = round( abs( rotY ) / polarStepSize ); + + // Get the index to the start of the right polar + // images for this viewing angle. + int numPolarOffset = numEquatorSteps * polarIdx; + + // Calculate the final image index for lookup + // of the texture coords. + index = ( rotZ / equatorStepSize ) + numPolarOffset; + } + + // Generate the final world space position. + outWsPosition = center + ( upVec * halfSize ) + ( rightVec * halfSize ); + + // Grab the uv set and setup the texture coord. + float4 uvSet = uvs[index]; + outTexCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + outTexCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + + // Needed for normal mapping and lighting. + outWorldToTangent[0] = float3( 1, 0, 0 ); + outWorldToTangent[1] = float3( 0, 1, 0 ); + outWorldToTangent[2] = float3( 0, 0, -1 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting.hlsl new file mode 100644 index 000000000..a41b8a873 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting.hlsl @@ -0,0 +1,249 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./torque.hlsl" + +#ifndef TORQUE_SHADERGEN + +// These are the uniforms used by most lighting shaders. + +uniform float4 inLightPos[3]; +uniform float4 inLightInvRadiusSq; +uniform float4 inLightColor[4]; + +#ifndef TORQUE_BL_NOSPOTLIGHT + uniform float4 inLightSpotDir[3]; + uniform float4 inLightSpotAngle; + uniform float4 inLightSpotFalloff; +#endif + +uniform float4 ambient; +#define ambientCameraFactor 0.3 +uniform float specularPower; +uniform float4 specularColor; + +#endif // !TORQUE_SHADERGEN + + +void compute4Lights( float3 wsView, + float3 wsPosition, + float3 wsNormal, + float4 shadowMask, + + #ifdef TORQUE_SHADERGEN + + float4 inLightPos[3], + float4 inLightInvRadiusSq, + float4 inLightColor[4], + float4 inLightSpotDir[3], + float4 inLightSpotAngle, + float4 inLightSpotFalloff, + float specularPower, + float4 specularColor, + + #endif // TORQUE_SHADERGEN + + out float4 outDiffuse, + out float4 outSpecular ) +{ + // NOTE: The light positions and spotlight directions + // are stored in SoA order, so inLightPos[0] is the + // x coord for all 4 lights... inLightPos[1] is y... etc. + // + // This is the key to fully utilizing the vector units and + // saving a huge amount of instructions. + // + // For example this change saved more than 10 instructions + // over a simple for loop for each light. + + int i; + + float4 lightVectors[3]; + for ( i = 0; i < 3; i++ ) + lightVectors[i] = wsPosition[i] - inLightPos[i]; + + float4 squareDists = 0; + for ( i = 0; i < 3; i++ ) + squareDists += lightVectors[i] * lightVectors[i]; + + // Accumulate the dot product between the light + // vector and the normal. + // + // The normal is negated because it faces away from + // the surface and the light faces towards the + // surface... this keeps us from needing to flip + // the light vector direction which complicates + // the spot light calculations. + // + // We normalize the result a little later. + // + float4 nDotL = 0; + for ( i = 0; i < 3; i++ ) + nDotL += lightVectors[i] * -wsNormal[i]; + + float4 rDotL = 0; + #ifndef TORQUE_BL_NOSPECULAR + + // We're using the Phong specular reflection model + // here where traditionally Torque has used Blinn-Phong + // which has proven to be more accurate to real materials. + // + // We do so because its cheaper as do not need to + // calculate the half angle for all 4 lights. + // + // Advanced Lighting still uses Blinn-Phong, but the + // specular reconstruction it does looks fairly similar + // to this. + // + float3 R = reflect( wsView, -wsNormal ); + + for ( i = 0; i < 3; i++ ) + rDotL += lightVectors[i] * R[i]; + + #endif + + // Normalize the dots. + // + // Notice we're using the half type here to get a + // much faster sqrt via the rsq_pp instruction at + // the loss of some precision. + // + // Unless we have some extremely large point lights + // i don't believe the precision loss will matter. + // + half4 correction = (half4)rsqrt( squareDists ); + nDotL = saturate( nDotL * correction ); + rDotL = clamp( rDotL * correction, 0.00001, 1.0 ); + + // First calculate a simple point light linear + // attenuation factor. + // + // If this is a directional light the inverse + // radius should be greater than the distance + // causing the attenuation to have no affect. + // + float4 atten = saturate( 1.0 - ( squareDists * inLightInvRadiusSq ) ); + + #ifndef TORQUE_BL_NOSPOTLIGHT + + // The spotlight attenuation factor. This is really + // fast for what it does... 6 instructions for 4 spots. + + float4 spotAtten = 0; + for ( i = 0; i < 3; i++ ) + spotAtten += lightVectors[i] * inLightSpotDir[i]; + + float4 cosAngle = ( spotAtten * correction ) - inLightSpotAngle; + atten *= saturate( cosAngle * inLightSpotFalloff ); + + #endif + + // Finally apply the shadow masking on the attenuation. + atten *= shadowMask; + + // Get the final light intensity. + float4 intensity = nDotL * atten; + + // Combine the light colors for output. + outDiffuse = 0; + for ( i = 0; i < 4; i++ ) + outDiffuse += intensity[i] * inLightColor[i]; + + // Output the specular power. + float4 specularIntensity = pow( rDotL, specularPower.xxxx ) * atten; + + // Apply the per-light specular attenuation. + float4 specular = float4(0,0,0,1); + for ( i = 0; i < 4; i++ ) + specular += float4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 ); + + // Add the final specular intensity values together + // using a single dot product operation then get the + // final specular lighting color. + outSpecular = specularColor * specular; +} + + +// This value is used in AL as a constant power to raise specular values +// to, before storing them into the light info buffer. The per-material +// specular value is then computer by using the integer identity of +// exponentiation: +// +// (a^m)^n = a^(m*n) +// +// or +// +// (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular) +// +#define AL_ConstantSpecularPower 12.0f + +/// The specular calculation used in Advanced Lighting. +/// +/// @param toLight Normalized vector representing direction from the pixel +/// being lit, to the light source, in world space. +/// +/// @param normal Normalized surface normal. +/// +/// @param toEye The normalized vector representing direction from the pixel +/// being lit to the camera. +/// +float AL_CalcSpecular( float3 toLight, float3 normal, float3 toEye ) +{ + // (R.V)^c + float specVal = dot( normalize( -reflect( toLight, normal ) ), toEye ); + + // Return the specular factor. + return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower ); +} + +/// The output for Deferred Lighting +/// +/// @param toLight Normalized vector representing direction from the pixel +/// being lit, to the light source, in world space. +/// +/// @param normal Normalized surface normal. +/// +/// @param toEye The normalized vector representing direction from the pixel +/// being lit to the camera. +/// +float4 AL_DeferredOutput( + float3 lightColor, + float3 diffuseColor, + float4 matInfo, + float4 ambient, + float specular, + float shadowAttenuation) +{ + float3 specularColor = float3(specular, specular, specular); + bool metalness = getFlag(matInfo.r, 3); + if ( metalness ) + { + specularColor = 0.04 * (1 - specular) + diffuseColor * specular; + } + + //specular = color * map * spec^gloss + float specularOut = (specularColor * matInfo.b * min(pow(abs(specular), max(( matInfo.a/ AL_ConstantSpecularPower),1.0f)),matInfo.a)).r; + + lightColor *= shadowAttenuation; + lightColor += ambient.rgb; + return float4(lightColor.rgb, specularOut); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/convexGeometryV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/convexGeometryV.hlsl new file mode 100644 index 000000000..064fcffa6 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/convexGeometryV.hlsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../hlslStructs.hlsl" +#include "../../shaderModel.hlsl" + +struct VertData +{ + float3 pos : POSITION; + float4 color : COLOR; +}; + +struct ConvexConnectV +{ + float4 hpos : TORQUE_POSITION; + float4 wsEyeDir : TEXCOORD0; + float4 ssPos : TEXCOORD1; + float4 vsEyeDir : TEXCOORD2; +}; + +ConvexConnectV main( VertData IN, + uniform float4x4 modelview, + uniform float4x4 objTrans, + uniform float4x4 worldViewOnly, + uniform float3 eyePosWorld ) +{ + ConvexConnectV OUT; + + OUT.hpos = mul( modelview, float4(IN.pos,1.0) ); + OUT.wsEyeDir = mul(objTrans, float4(IN.pos, 1.0)) - float4(eyePosWorld, 0.0); + OUT.vsEyeDir = mul(worldViewOnly, float4(IN.pos, 1.0)); + OUT.ssPos = OUT.hpos; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgColorBufferP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgColorBufferP.hlsl new file mode 100644 index 000000000..ad3debbaf --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgColorBufferP.hlsl @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + return float4(TORQUE_TEX2D( colorBufferTex, IN.uv0 ).rgb, 1.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl new file mode 100644 index 000000000..68df09a78 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); +TORQUE_UNIFORM_SAMPLER1D(depthViz, 1); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float depth = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).w; + return float4( TORQUE_TEX1D( depthViz, depth ).rgb, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgGlowVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgGlowVisualizeP.hlsl new file mode 100644 index 000000000..257383659 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgGlowVisualizeP.hlsl @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(glowBuffer, 0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + return TORQUE_TEX2D(glowBuffer, IN.uv0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl new file mode 100644 index 000000000..ca6d8d677 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModelAutoGen.hlsl" +#include "../../postfx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(lightPrePassTex,0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 lightColor = TORQUE_TEX2D( lightPrePassTex, IN.uv0 ); + return float4( lightColor.rgb, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl new file mode 100644 index 000000000..072f07e00 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(lightPrePassTex,0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float specular = TORQUE_TEX2D( lightPrePassTex, IN.uv0 ).a; + return float4( specular, specular, specular, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl new file mode 100644 index 000000000..4f31d2c53 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float3 normal = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).xyz; + return float4( ( normal + 1.0 ) * 0.5, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgShadowVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgShadowVisualizeP.hlsl new file mode 100644 index 000000000..b54833499 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgShadowVisualizeP.hlsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +struct MaterialDecoratorConnectV +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(shadowMap, 0); +TORQUE_UNIFORM_SAMPLER1D(depthViz, 1); + +float4 main( MaterialDecoratorConnectV IN ) : TORQUE_TARGET0 +{ + float depth = saturate( TORQUE_TEX2D( shadowMap, IN.uv0 ).r ); + return float4( TORQUE_TEX1D( depthViz, depth ).rgb, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgSpecMapVisualizeP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgSpecMapVisualizeP.hlsl new file mode 100644 index 000000000..eba38a879 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/dbgSpecMapVisualizeP.hlsl @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../postfx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(matinfoTex,0); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float specular = TORQUE_TEX2D( matinfoTex, IN.uv0 ).b; + return float4( specular, specular, specular, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferP.hlsl new file mode 100644 index 000000000..cefebe8c7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferP.hlsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +struct Conn +{ + float4 hpos : TORQUE_POSITION; +}; + +struct Fragout +{ + float4 col : TORQUE_TARGET0; + float4 col1 : TORQUE_TARGET1; + float4 col2 : TORQUE_TARGET2; +}; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main( Conn IN ) +{ + Fragout OUT; + + // Clear Prepass Buffer ( Normals/Depth ); + OUT.col = float4(1.0, 1.0, 1.0, 1.0); + + // Clear Color Buffer. + OUT.col1 = float4(0.0, 0.0, 0.0, 1.0); + + // Clear Material Info Buffer. + OUT.col2 = float4(0.0, 0.0, 0.0, 1.0); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferV.hlsl new file mode 100644 index 000000000..20ba4d509 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredClearGBufferV.hlsl @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +struct Appdata +{ + float3 pos : POSITION; + float4 color : COLOR; +}; + +struct Conn +{ + float4 hpos : TORQUE_POSITION; +}; + +uniform float4x4 modelview; + +Conn main( Appdata In ) +{ + Conn Out; + Out.hpos = float4(In.pos,1.0); + return Out; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredColorShaderP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredColorShaderP.hlsl new file mode 100644 index 000000000..d91d2eb38 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredColorShaderP.hlsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +struct Fragout +{ + float4 col : TORQUE_TARGET0; + float4 col1 : TORQUE_TARGET1; + float4 col2 : TORQUE_TARGET2; +}; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main( ) +{ + Fragout OUT; + + OUT.col = float4(0.0, 0.0, 0.0, 0.0); + OUT.col1 = float4(1.0, 1.0, 1.0, 1.0); + + // Draw on color buffer. + OUT.col2 = float4(1.0, 0.0, 0.0, 1.0); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredShadingP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredShadingP.hlsl new file mode 100644 index 000000000..338ebd8da --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/deferredShadingP.hlsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModelAutoGen.hlsl" +#include "../../postfx/postFx.hlsl" +#include "../../torque.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(colorBufferTex,0); +TORQUE_UNIFORM_SAMPLER2D(lightPrePassTex,1); +TORQUE_UNIFORM_SAMPLER2D(matInfoTex,2); +TORQUE_UNIFORM_SAMPLER2D(prepassTex,3); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 lightBuffer = TORQUE_TEX2D( lightPrePassTex, IN.uv0 ); + float4 colorBuffer = TORQUE_TEX2D( colorBufferTex, IN.uv0 ); + float4 matInfo = TORQUE_TEX2D( matInfoTex, IN.uv0 ); + float specular = saturate(lightBuffer.a); + float depth = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).w; + + if (depth>0.9999) + return float4(0,0,0,0); + + // Diffuse Color Altered by Metalness + bool metalness = getFlag(matInfo.r, 3); + if ( metalness ) + { + colorBuffer *= (1.0 - colorBuffer.a); + } + + colorBuffer += float4(specular, specular, specular, 1.0); + colorBuffer *= float4(lightBuffer.rgb, 1.0); + + return hdrEncode( float4(colorBuffer.rgb, 1.0) ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuad.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuad.hlsl new file mode 100644 index 000000000..543e21677 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuad.hlsl @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../shaderModel.hlsl" + +struct FarFrustumQuadConnectV +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; + float3 wsEyeRay : TEXCOORD1; + float3 vsEyeRay : TEXCOORD2; +}; + +struct FarFrustumQuadConnectP +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; + float3 wsEyeRay : TEXCOORD1; + float3 vsEyeRay : TEXCOORD2; +}; + + +float2 getUVFromSSPos( float3 ssPos, float4 rtParams ) +{ + float2 outPos = ( ssPos.xy + 1.0 ) / 2.0; + outPos.y = 1.0 - outPos.y; + outPos = ( outPos * rtParams.zw ) + rtParams.xy; + return outPos; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuadV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuadV.hlsl new file mode 100644 index 000000000..0167d901a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/farFrustumQuadV.hlsl @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../hlslStructs.hlsl" +#include "farFrustumQuad.hlsl" + + +FarFrustumQuadConnectV main( VertexIn_PNTT IN, + uniform float4 rtParams0 ) +{ + FarFrustumQuadConnectV OUT; + + OUT.hpos = float4( IN.uv0, 0, 1 ); + + // Get a RT-corrected UV from the SS coord + OUT.uv0 = getUVFromSSPos( OUT.hpos.xyz, rtParams0 ); + + // Interpolators will generate eye rays the + // from far-frustum corners. + OUT.wsEyeRay = IN.tangent; + OUT.vsEyeRay = IN.normal; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/convexGeometryV.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/convexGeometryV.glsl new file mode 100644 index 000000000..1807ac43f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/convexGeometryV.glsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; + +#define IN_pos vPosition + +out vec4 wsEyeDir; +out vec4 ssPos; +out vec4 vsEyeDir; + +#define OUT_hpos gl_Position +#define OUT_wsEyeDir wsEyeDir +#define OUT_ssPos ssPos +#define OUT_vsEyeDir vsEyeDir + +uniform mat4 modelview; +uniform mat4 objTrans; +uniform mat4 worldViewOnly; +uniform vec3 eyePosWorld; + +void main() +{ + OUT_hpos = tMul( modelview, IN_pos ); + OUT_wsEyeDir = tMul( objTrans, IN_pos ) - vec4( eyePosWorld, 0.0 ); + OUT_vsEyeDir = tMul( worldViewOnly, IN_pos ); + OUT_ssPos = OUT_hpos; + + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgColorBufferP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgColorBufferP.glsl new file mode 100644 index 000000000..12962e798 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgColorBufferP.glsl @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D colorBufferTex; + +out vec4 OUT_FragColor0; + +void main() +{ + OUT_FragColor0 = vec4(texture( colorBufferTex, uv0 ).rgb, 1.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl new file mode 100644 index 000000000..6b9dd72ad --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec2 uv0; +uniform sampler2D prepassTex; +uniform sampler1D depthViz; + +out vec4 OUT_col; + +void main() +{ + float depth = prepassUncondition( prepassTex, uv0 ).w; + OUT_col = vec4( texture( depthViz, depth ).rgb, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgGlowVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgGlowVisualizeP.glsl new file mode 100644 index 000000000..355e6ef53 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgGlowVisualizeP.glsl @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D glowBuffer; + +out vec4 OUT_FragColor0; + +void main() +{ + OUT_FragColor0 = texture(glowBuffer, uv0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl new file mode 100644 index 000000000..3e7de5a66 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D lightPrePassTex; + +out vec4 OUT_col; + +void main() +{ + vec4 lightColor = texture( lightPrePassTex, uv0 ); + OUT_col = vec4( lightColor.rgb, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl new file mode 100644 index 000000000..7b654c936 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D lightPrePassTex; + +out vec4 OUT_col; + +void main() +{ + float specular = texture( lightPrePassTex, uv0 ).a; + OUT_col = vec4( specular, specular, specular, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl new file mode 100644 index 000000000..84ea4d3fb --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec2 uv0; +uniform sampler2D prepassTex; + +out vec4 OUT_col; + +void main() +{ + vec3 normal = prepassUncondition( prepassTex, uv0 ).xyz; + OUT_col = vec4( ( normal + 1.0 ) * 0.5, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl new file mode 100644 index 000000000..b51e7310a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D shadowMap; +uniform sampler1D depthViz; + +out vec4 OUT_col; + +void main() +{ + float depth = saturate( texture( shadowMap, uv0 ).r ); + OUT_col = vec4( texture( depthViz, depth ).rgb, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgSpecMapVisualizeP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgSpecMapVisualizeP.glsl new file mode 100644 index 000000000..3e5efd675 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/dbgSpecMapVisualizeP.glsl @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +uniform sampler2D matinfoTex; + +out vec4 OUT_FragColor0; + +void main() +{ + float specular = texture( matinfoTex, uv0 ).a; + OUT_FragColor0 = vec4( specular, specular, specular, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl new file mode 100644 index 000000000..39dc0dc9f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredClearGBufferP.glsl @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +out vec4 OUT_col; +out vec4 OUT_col1; +out vec4 OUT_col2; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + // Clear Prepass Buffer ( Normals/Depth ); + OUT_col = vec4(1.0, 1.0, 1.0, 1.0); + + // Clear Color Buffer. + OUT_col1 = vec4(0.0, 0.0, 0.0, 1.0); + + // Clear Material Info Buffer. + OUT_col2 = vec4(0.0, 0.0, 0.0, 1.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredColorShaderP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredColorShaderP.glsl new file mode 100644 index 000000000..85c553089 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredColorShaderP.glsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +layout (location = 0) out vec4 col; +layout (location = 1) out vec4 col1; +layout (location = 2) out vec4 col2; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + col = vec4(0.0, 0.0, 0.0, 0.0); + col1 = vec4(1.0, 1.0, 1.0, 1.0); + + // Draw on color buffer. + col2 = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredShadingP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredShadingP.glsl new file mode 100644 index 000000000..ae01125af --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/deferredShadingP.glsl @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../../postFx/gl/postFX.glsl" +#include "../../../gl/torque.glsl" + +uniform sampler2D colorBufferTex; +uniform sampler2D lightPrePassTex; +uniform sampler2D matInfoTex; +uniform sampler2D prepassTex; + +out vec4 OUT_col; + +void main() +{ + float depth = prepassUncondition( prepassTex, uv0 ).w; + if (depth>0.9999) + { + OUT_col = vec4(0.0); + return; + } + vec4 lightBuffer = texture( lightPrePassTex, uv0 ); + vec4 colorBuffer = texture( colorBufferTex, uv0 ); + vec4 matInfo = texture( matInfoTex, uv0 ); + float specular = clamp(lightBuffer.a,0.0,1.0); + + // Diffuse Color Altered by Metalness + bool metalness = getFlag(matInfo.r, 3); + if ( metalness ) + { + colorBuffer *= (1.0 - colorBuffer.a); + } + + colorBuffer += vec4(specular, specular, specular, 1.0); + colorBuffer *= vec4(lightBuffer.rgb, 1.0); + + OUT_col = hdrEncode( vec4(colorBuffer.rgb, 1.0) ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl new file mode 100644 index 000000000..76054eb09 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +vec2 getUVFromSSPos( vec3 ssPos, vec4 rtParams ) +{ + vec2 outPos = ( ssPos.xy + 1.0 ) / 2.0; + outPos.y = 1.0 - outPos.y; + outPos = ( outPos * rtParams.zw ) + rtParams.xy; + return outPos; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl new file mode 100644 index 000000000..a80e856ed --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "farFrustumQuad.glsl" + +in vec4 vPosition; +in vec3 vNormal; +in vec3 vTangent; +in vec2 vTexCoord0; + +uniform vec4 rtParams0; +out vec4 hpos; +out vec2 uv0; +out vec3 wsEyeRay; +out vec3 vsEyeRay; + +void main() +{ + hpos = vec4( vTexCoord0, 0, 1 ); + + // Get a RT-corrected UV from the SS coord + uv0 = getUVFromSSPos( hpos.xyz, rtParams0 ); + gl_Position = hpos; + + // Interpolators will generate eye rays the + // from far-frustum corners. + wsEyeRay = vTangent; + vsEyeRay = vNormal; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/lightingUtils.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/lightingUtils.glsl new file mode 100644 index 000000000..08af9231b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/lightingUtils.glsl @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +float attenuate( vec4 lightColor, vec2 attParams, float dist ) +{ + // We're summing the results of a scaled constant, + // linear, and quadratic attenuation. + + #ifdef ACCUMULATE_LUV + return lightColor.w * ( 1.0 - dot( attParams, vec2( dist, dist * dist ) ) ); + #else + return 1.0 - dot( attParams, vec2( dist, dist * dist ) ); + #endif +} + +// Calculate the specular coefficent +// +// pxlToLight - Normalized vector representing direction from the pixel being lit, to the light source, in world space +// normal - Normalized surface normal +// pxlToEye - Normalized vector representing direction from pixel being lit, to the camera, in world space +// specPwr - Specular exponent +// specularScale - A scalar on the specular output used in RGB accumulation. +// +float calcSpecular( vec3 pxlToLight, vec3 normal, vec3 pxlToEye, float specPwr, float specularScale ) +{ +#ifdef PHONG_SPECULAR + // (R.V)^c + float specVal = dot( normalize( -reflect( pxlToLight, normal ) ), pxlToEye ); +#else + // (N.H)^c [Blinn-Phong, TGEA style, default] + float specVal = dot( normal, normalize( pxlToLight + pxlToEye ) ); +#endif + +#ifdef ACCUMULATE_LUV + return pow( max( specVal, 0.00001f ), specPwr ); +#else + // If this is RGB accumulation, than there is no facility for the luminance + // of the light to play in to the specular intensity. In LUV, the luminance + // of the light color gets rolled into N.L * Attenuation + return specularScale * pow( max( specVal, 0.00001f ), specPwr ); +#endif +} + +vec3 getDistanceVectorToPlane( vec3 origin, vec3 direction, vec4 plane ) +{ + float denum = dot( plane.xyz, direction.xyz ); + float num = dot( plane, vec4( origin, 1.0 ) ); + float t = -num / denum; + + return direction.xyz * t; +} + +vec3 getDistanceVectorToPlane( float negFarPlaneDotEye, vec3 direction, vec4 plane ) +{ + float denum = dot( plane.xyz, direction.xyz ); + float t = negFarPlaneDotEye / denum; + + return direction.xyz * t; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/pointLightP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/pointLightP.glsl new file mode 100644 index 000000000..8fe127e04 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/pointLightP.glsl @@ -0,0 +1,273 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +#include "farFrustumQuad.glsl" +#include "lightingUtils.glsl" +#include "../../../gl/lighting.glsl" +#include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" +#include "../../../gl/torque.glsl" + +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; +in vec4 color; + +#ifdef USE_COOKIE_TEX + +/// The texture for cookie rendering. +uniform samplerCube cookieMap ; + +#endif + + +#ifdef SHADOW_CUBE + + vec3 decodeShadowCoord( vec3 shadowCoord ) + { + return shadowCoord; + } + + vec4 shadowSample( samplerCube shadowMap, vec3 shadowCoord ) + { + return texture( shadowMap, shadowCoord ); + } + +#else + + vec3 decodeShadowCoord( vec3 paraVec ) + { + // Flip y and z + paraVec = paraVec.xzy; + + #ifndef SHADOW_PARABOLOID + + bool calcBack = (paraVec.z < 0.0); + if ( calcBack ) + { + paraVec.z = paraVec.z * -1.0; + + #ifdef SHADOW_DUALPARABOLOID + paraVec.x = -paraVec.x; + #endif + } + + #endif + + vec3 shadowCoord; + shadowCoord.x = (paraVec.x / (2*(1 + paraVec.z))) + 0.5; + shadowCoord.y = 1-((paraVec.y / (2*(1 + paraVec.z))) + 0.5); + shadowCoord.z = 0; + + // adjust the co-ordinate slightly if it is near the extent of the paraboloid + // this value was found via experementation + // NOTE: this is wrong, it only biases in one direction, not towards the uv + // center ( 0.5 0.5 ). + //shadowCoord.xy *= 0.997; + + #ifndef SHADOW_PARABOLOID + + // If this is the back, offset in the atlas + if ( calcBack ) + shadowCoord.x += 1.0; + + // Atlasing front and back maps, so scale + shadowCoord.x *= 0.5; + + #endif + + return shadowCoord; + } + +#endif + +uniform sampler2D prePassBuffer; + +#ifdef SHADOW_CUBE + uniform samplerCube shadowMap; +#else + uniform sampler2D shadowMap; + uniform sampler2D dynamicShadowMap; +#endif + +uniform sampler2D lightBuffer; +uniform sampler2D colorBuffer; +uniform sampler2D matInfoBuffer; + +uniform vec4 rtParams0; + +uniform vec3 lightPosition; +uniform vec4 lightColor; +uniform float lightBrightness; +uniform float lightRange; +uniform vec2 lightAttenuation; +uniform vec4 lightMapParams; +uniform vec4 vsFarPlane; +uniform mat3 viewToLightProj; +uniform mat3 dynamicViewToLightProj; +uniform vec4 lightParams; +uniform float shadowSoftness; + +out vec4 OUT_col; + +void main() +{ + // Compute scene UV + vec3 ssPos = ssPos.xyz / ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); + + // Emissive. + vec4 matInfo = texture( matInfoBuffer, uvScene ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + OUT_col = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + + vec4 colorSample = texture( colorBuffer, uvScene ); + vec3 subsurface = vec3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = vec3(0.772549, 0.337255, 0.262745); + else + subsurface = vec3(0.337255, 0.772549, 0.262745); + } + + // Sample/unpack the normal/z data + vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); + vec3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Eye ray - Eye -> Pixel + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; + + // Build light vec, get length, clip pixel if needed + vec3 lightVec = lightPosition - viewSpacePos; + float lenLightV = length( lightVec ); + clip( lightRange - lenLightV ); + + // Get the attenuated falloff. + float atten = attenuate( lightColor, lightAttenuation, lenLightV ); + clip( atten - 1e-6 ); + + // Normalize lightVec + lightVec /= lenLightV; + + // If we can do dynamic branching then avoid wasting + // fillrate on pixels that are backfacing to the light. + float nDotL = dot( lightVec, normal ); + //DB_CLIP( nDotL < 0 ); + + #ifdef NO_SHADOW + + float shadowed = 1.0; + + #else + + // Get a linear depth from the light source. + float distToLight = lenLightV / lightRange; + + #ifdef SHADOW_CUBE + + // TODO: We need to fix shadow cube to handle soft shadows! + float occ = texture( shadowMap, tMul( viewToLightProj, -lightVec ) ).r; + float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) ); + + #else + + vec2 shadowCoord = decodeShadowCoord( tMul( viewToLightProj, -lightVec ) ).xy; + + float static_shadowed = softShadow_filter( shadowMap, + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + vec2 dynamicShadowCoord = decodeShadowCoord( tMul( dynamicViewToLightProj, -lightVec ) ).xy; + float dynamic_shadowed = softShadow_filter( dynamicShadowMap, + ssPos.xy, + dynamicShadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + float shadowed = min(static_shadowed, dynamic_shadowed); + #endif + + #endif // !NO_SHADOW + + vec3 lightcol = lightColor.rgb; + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, tMul( viewToLightProj, -lightVec ) ); + + // Multiply the light with the cookie tex. + lightcol *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + + // NOTE: Do not clip on fully shadowed pixels as it would + // cause the hardware occlusion query to disable the shadow. + + // Specular term + float specular = AL_CalcSpecular( lightVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; + + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightcol; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/softShadow.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/softShadow.glsl new file mode 100644 index 000000000..a14213946 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/softShadow.glsl @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +#if defined( SOFTSHADOW ) && defined( SOFTSHADOW_HIGH_QUALITY ) + +#define NUM_PRE_TAPS 4 +#define NUM_TAPS 12 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_TAPS] = vec2[] +( + // These first 4 taps are located around the edges + // of the disk and are used to predict fully shadowed + // or unshadowed areas. + vec2( 0.992833, 0.979309 ), + vec2( -0.998585, 0.985853 ), + vec2( 0.949299, -0.882562 ), + vec2( -0.941358, -0.893924 ), + + // The rest of the samples. + vec2( 0.545055, -0.589072 ), + vec2( 0.346526, 0.385821 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.248676, -0.679605 ), + vec2( -0.569502, -0.390637 ), + vec2( -0.614096, 0.212577 ), + vec2( -0.259178, 0.876272 ), + vec2( 0.649526, 0.864333 ) +); + +#else + +#define NUM_PRE_TAPS 5 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_PRE_TAPS] = vec2[] +( + vec2( 0.892833, 0.959309 ), + vec2( -0.941358, -0.873924 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.348676, -0.679605 ), + vec2( -0.569502, -0.390637 ) +); + +#endif + + +/// The texture used to do per-pixel pseudorandom +/// rotations of the filter taps. +uniform sampler2D gTapRotationTex ; + + +float softShadow_sampleTaps( sampler2D shadowMap, + vec2 sinCos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float esmFactor, + int startTap, + int endTap ) +{ + float shadow = 0; + + vec2 tap = vec2(0); + for ( int t = startTap; t < endTap; t++ ) + { + tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; + tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; + float occluder = tex2Dlod( shadowMap, vec4( shadowPos + tap, 0, 0 ) ).r; + + float esm = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + shadow += esm / float( endTap - startTap ); + } + + return shadow; +} + + +float softShadow_filter( sampler2D shadowMap, + vec2 vpos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float dotNL, + float esmFactor ) +{ + #ifndef SOFTSHADOW + + // If softshadow is undefined then we skip any complex + // filtering... just do a single sample ESM. + + float occluder = tex2Dlod( shadowMap, vec4( shadowPos, 0, 0 ) ).r; + float shadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + + #else + + // Lookup the random rotation for this screen pixel. + vec2 sinCos = ( tex2Dlod( gTapRotationTex, vec4( vpos * 16, 0, 0 ) ).rg - 0.5 ) * 2; + + // Do the prediction taps first. + float shadow = softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + 0, + NUM_PRE_TAPS ); + + // We live with only the pretap results if we don't + // have high quality shadow filtering enabled. + #ifdef SOFTSHADOW_HIGH_QUALITY + + // Only do the expensive filtering if we're really + // in a partially shadowed area. + if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0 ) > 0.06 ) + { + shadow += softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + NUM_PRE_TAPS, + NUM_TAPS ); + + // This averages the taps above with the results + // of the prediction samples. + shadow *= 0.5; + } + + #endif // SOFTSHADOW_HIGH_QUALITY + + #endif // SOFTSHADOW + + return shadow; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/spotLightP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/spotLightP.glsl new file mode 100644 index 000000000..c6ffa02a0 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/spotLightP.glsl @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "farFrustumQuad.glsl" +#include "lightingUtils.glsl" +#include "../../shadowMap/shadowMapIO_GLSL.h" +#include "shadergen:/autogenConditioners.h" +#include "softShadow.glsl" +#include "../../../gl/lighting.glsl" +#include "../../../gl/torque.glsl" + +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; +in vec4 color; + +#define IN_wsEyeDir wsEyeDir +#define IN_ssPos ssPos +#define IN_vsEyeDir vsEyeDir +#define IN_color color + +#ifdef USE_COOKIE_TEX + +/// The texture for cookie rendering. +uniform sampler2D cookieMap; + +#endif + +uniform sampler2D prePassBuffer; +uniform sampler2D shadowMap; +uniform sampler2D dynamicShadowMap; + +uniform sampler2D lightBuffer; +uniform sampler2D colorBuffer; +uniform sampler2D matInfoBuffer; + +uniform vec4 rtParams0; + +uniform vec3 lightPosition; +uniform vec4 lightColor; +uniform float lightBrightness; +uniform float lightRange; +uniform vec2 lightAttenuation; +uniform vec3 lightDirection; +uniform vec4 lightSpotParams; +uniform vec4 lightMapParams; + +uniform vec4 vsFarPlane; +uniform mat4 viewToLightProj; +uniform mat4 dynamicViewToLightProj; + +uniform vec4 lightParams; +uniform float shadowSoftness; + +out vec4 OUT_col; + +void main() +{ + // Compute scene UV + vec3 ssPos = IN_ssPos.xyz / IN_ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); + + // Emissive. + vec4 matInfo = texture( matInfoBuffer, uvScene ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + OUT_col = vec4(0.0, 0.0, 0.0, 0.0); + return; + } + + vec4 colorSample = texture( colorBuffer, uvScene ); + vec3 subsurface = vec3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = vec3(0.772549, 0.337255, 0.262745); + else + subsurface = vec3(0.337255, 0.772549, 0.262745); + } + + // Sample/unpack the normal/z data + vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); + vec3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Eye ray - Eye -> Pixel + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN_vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; + + // Build light vec, get length, clip pixel if needed + vec3 lightToPxlVec = viewSpacePos - lightPosition; + float lenLightV = length( lightToPxlVec ); + lightToPxlVec /= lenLightV; + + //lightDirection = vec3( -lightDirection.xy, lightDirection.z ); //vec3( 0, 0, -1 ); + float cosAlpha = dot( lightDirection, lightToPxlVec ); + clip( cosAlpha - lightSpotParams.x ); + clip( lightRange - lenLightV ); + + float atten = attenuate( lightColor, lightAttenuation, lenLightV ); + atten *= ( cosAlpha - lightSpotParams.x ) / lightSpotParams.y; + clip( atten - 1e-6 ); + atten = saturate( atten ); + + float nDotL = dot( normal, -lightToPxlVec ); + + // Get the shadow texture coordinate + vec4 pxlPosLightProj = tMul( viewToLightProj, vec4( viewSpacePos, 1 ) ); + vec2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); + shadowCoord.y = 1.0f - shadowCoord.y; + + // Get the dynamic shadow texture coordinate + vec4 dynpxlPosLightProj = tMul( dynamicViewToLightProj, vec4( viewSpacePos, 1 ) ); + vec2 dynshadowCoord = ( ( dynpxlPosLightProj.xy / dynpxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); + dynshadowCoord.y = 1.0f - dynshadowCoord.y; + #ifdef NO_SHADOW + + float shadowed = 1.0; + + #else + + // Get a linear depth from the light source. + float distToLight = pxlPosLightProj.z / lightRange; + + float static_shadowed = softShadow_filter( shadowMap, + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + float dynamic_shadowed = softShadow_filter( dynamicShadowMap, + ssPos.xy, + dynshadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + float shadowed = min(static_shadowed, dynamic_shadowed); + #endif // !NO_SHADOW + + vec3 lightcol = lightColor.rgb; + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, shadowCoord ); + + // Multiply the light with the cookie tex. + lightcol *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + + // NOTE: Do not clip on fully shadowed pixels as it would + // cause the hardware occlusion query to disable the shadow. + + // Specular term + float specular = AL_CalcSpecular( -lightToPxlVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; + + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightcol; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/vectorLightP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/vectorLightP.glsl new file mode 100644 index 000000000..15e0bf477 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/gl/vectorLightP.glsl @@ -0,0 +1,327 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "farFrustumQuad.glsl" +#include "../../../gl/torque.glsl" +#include "../../../gl/lighting.glsl" +#include "lightingUtils.glsl" +#include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" + +in vec4 hpos; +in vec2 uv0; +in vec3 wsEyeRay; +in vec3 vsEyeRay; + +uniform sampler2D shadowMap; +uniform sampler2D dynamicShadowMap; + +#ifdef USE_SSAO_MASK +uniform sampler2D ssaoMask ; +uniform vec4 rtParams3; +#endif + +uniform sampler2D prePassBuffer; +uniform sampler2D lightBuffer; +uniform sampler2D colorBuffer; +uniform sampler2D matInfoBuffer; +uniform vec3 lightDirection; +uniform vec4 lightColor; +uniform float lightBrightness; +uniform vec4 lightAmbient; +uniform vec3 eyePosWorld; +uniform mat4x4 eyeMat; +uniform vec4 atlasXOffset; +uniform vec4 atlasYOffset; +uniform vec2 atlasScale; +uniform vec4 zNearFarInvNearFar; +uniform vec4 lightMapParams; +uniform vec2 fadeStartLength; +uniform vec4 overDarkPSSM; +uniform float shadowSoftness; + +//static shadowMap +uniform mat4x4 worldToLightProj; +uniform vec4 scaleX; +uniform vec4 scaleY; +uniform vec4 offsetX; +uniform vec4 offsetY; +uniform vec4 farPlaneScalePSSM; + +//dynamic shadowMap +uniform mat4x4 dynamicWorldToLightProj; +uniform vec4 dynamicScaleX; +uniform vec4 dynamicScaleY; +uniform vec4 dynamicOffsetX; +uniform vec4 dynamicOffsetY; +uniform vec4 dynamicFarPlaneScalePSSM; + +vec4 AL_VectorLightShadowCast( sampler2D _sourceshadowMap, + vec2 _texCoord, + mat4 _worldToLightProj, + vec4 _worldPos, + vec4 _scaleX, vec4 _scaleY, + vec4 _offsetX, vec4 _offsetY, + vec4 _farPlaneScalePSSM, + vec4 _atlasXOffset, vec4 _atlasYOffset, + vec2 _atlasScale, + float _shadowSoftness, + float _dotNL , + vec4 _overDarkPSSM +) +{ + + // Compute shadow map coordinate + vec4 pxlPosLightProj = tMul(_worldToLightProj, _worldPos); + vec2 baseShadowCoord = pxlPosLightProj.xy / pxlPosLightProj.w; + + // Distance to light, in shadowMap space + float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; + + // Figure out which split to sample from. Basically, we compute the shadowMap sample coord + // for all of the splits and then check if its valid. + vec4 shadowCoordX = vec4( baseShadowCoord.x ); + vec4 shadowCoordY = vec4( baseShadowCoord.y ); + vec4 farPlaneDists = vec4( distToLight ); + shadowCoordX *= _scaleX; + shadowCoordY *= _scaleY; + shadowCoordX += _offsetX; + shadowCoordY += _offsetY; + farPlaneDists *= _farPlaneScalePSSM; + + // If the shadow sample is within -1..1 and the distance + // to the light for this pixel is less than the far plane + // of the split, use it. + vec4 finalMask; + if ( shadowCoordX.x > -0.99 && shadowCoordX.x < 0.99 && + shadowCoordY.x > -0.99 && shadowCoordY.x < 0.99 && + farPlaneDists.x < 1.0 ) + finalMask = vec4(1, 0, 0, 0); + + else if ( shadowCoordX.y > -0.99 && shadowCoordX.y < 0.99 && + shadowCoordY.y > -0.99 && shadowCoordY.y < 0.99 && + farPlaneDists.y < 1.0 ) + finalMask = vec4(0, 1, 0, 0); + + else if ( shadowCoordX.z > -0.99 && shadowCoordX.z < 0.99 && + shadowCoordY.z > -0.99 && shadowCoordY.z < 0.99 && + farPlaneDists.z < 1.0 ) + finalMask = vec4(0, 0, 1, 0); + + else + finalMask = vec4(0, 0, 0, 1); + + vec3 debugColor = vec3(0); + + #ifdef NO_SHADOW + debugColor = vec3(1.0); + #endif + + #ifdef PSSM_DEBUG_RENDER + if ( finalMask.x > 0 ) + debugColor += vec3( 1, 0, 0 ); + else if ( finalMask.y > 0 ) + debugColor += vec3( 0, 1, 0 ); + else if ( finalMask.z > 0 ) + debugColor += vec3( 0, 0, 1 ); + else if ( finalMask.w > 0 ) + debugColor += vec3( 1, 1, 0 ); + #endif + + // Here we know what split we're sampling from, so recompute the _texCoord location + // Yes, we could just use the result from above, but doing it this way actually saves + // shader instructions. + vec2 finalScale; + finalScale.x = dot(finalMask, _scaleX); + finalScale.y = dot(finalMask, _scaleY); + + vec2 finalOffset; + finalOffset.x = dot(finalMask, _offsetX); + finalOffset.y = dot(finalMask, _offsetY); + + vec2 shadowCoord; + shadowCoord = baseShadowCoord * finalScale; + shadowCoord += finalOffset; + + // Convert to _texCoord space + shadowCoord = 0.5 * shadowCoord + vec2(0.5, 0.5); + shadowCoord.y = 1.0f - shadowCoord.y; + + // Move around inside of atlas + vec2 aOffset; + aOffset.x = dot(finalMask, _atlasXOffset); + aOffset.y = dot(finalMask, _atlasYOffset); + + shadowCoord *= _atlasScale; + shadowCoord += aOffset; + + // Each split has a different far plane, take this into account. + float farPlaneScale = dot( _farPlaneScalePSSM, finalMask ); + distToLight *= farPlaneScale; + + return vec4(debugColor, + softShadow_filter( _sourceshadowMap, + _texCoord, + shadowCoord, + farPlaneScale * _shadowSoftness, + distToLight, + _dotNL, + dot( finalMask, _overDarkPSSM ) ) ); +} + +out vec4 OUT_col; +void main() +{ + // Emissive. + float4 matInfo = texture( matInfoBuffer, uv0 ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + OUT_col = vec4(1.0, 1.0, 1.0, 0.0); + return; + } + + vec4 colorSample = texture( colorBuffer, uv0 ); + vec3 subsurface = vec3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = vec3(0.772549, 0.337255, 0.262745); + else + subsurface = vec3(0.337255, 0.772549, 0.262745); + } + + // Sample/unpack the normal/z data + vec4 prepassSample = prepassUncondition( prePassBuffer, uv0 ); + vec3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Use eye ray to get ws pos + vec4 worldPos = vec4(eyePosWorld + wsEyeRay * depth, 1.0f); + + // Get the light attenuation. + float dotNL = dot(-lightDirection, normal); + + #ifdef PSSM_DEBUG_RENDER + vec3 debugColor = vec3(0); + #endif + + #ifdef NO_SHADOW + + // Fully unshadowed. + float shadowed = 1.0; + + #ifdef PSSM_DEBUG_RENDER + debugColor = vec3(1.0); + #endif + + #else + + vec4 static_shadowed_colors = AL_VectorLightShadowCast( shadowMap, + uv0.xy, + worldToLightProj, + worldPos, + scaleX, scaleY, + offsetX, offsetY, + farPlaneScalePSSM, + atlasXOffset, atlasYOffset, + atlasScale, + shadowSoftness, + dotNL, + overDarkPSSM); + vec4 dynamic_shadowed_colors = AL_VectorLightShadowCast( dynamicShadowMap, + uv0.xy, + dynamicWorldToLightProj, + worldPos, + dynamicScaleX, dynamicScaleY, + dynamicOffsetX, dynamicOffsetY, + dynamicFarPlaneScalePSSM, + atlasXOffset, atlasYOffset, + atlasScale, + shadowSoftness, + dotNL, + overDarkPSSM); + float static_shadowed = static_shadowed_colors.a; + float dynamic_shadowed = dynamic_shadowed_colors.a; + + #ifdef PSSM_DEBUG_RENDER + debugColor = static_shadowed_colors.rgb*0.5+dynamic_shadowed_colors.rgb*0.5; + #endif + + // Fade out the shadow at the end of the range. + vec4 zDist = vec4(zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); + float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y; + + static_shadowed = mix( static_shadowed, 1.0, saturate( fadeOutAmt ) ); + dynamic_shadowed = mix( dynamic_shadowed, 1.0, saturate( fadeOutAmt ) ); + + // temp for debugging. uncomment one or the other. + //float shadowed = static_shadowed; + //float shadowed = dynamic_shadowed; + float shadowed = min(static_shadowed, dynamic_shadowed); + + #ifdef PSSM_DEBUG_RENDER + if ( fadeOutAmt > 1.0 ) + debugColor = vec3(1.0); + #endif + + #endif // !NO_SHADOW + + // Specular term + float specular = AL_CalcSpecular( -lightDirection, + normal, + normalize(-vsEyeRay) ) * lightBrightness * shadowed; + + float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = (lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * saturate(dot(normalize(-vsEyeRay), normal)) ); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = dotNL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + // Sample the AO texture. + #ifdef USE_SSAO_MASK + float ao = 1.0 - texture( ssaoMask, viewportCoordToRenderTarget( uv0.xy, rtParams3 ) ).r; + addToResult *= ao; + #endif + + #ifdef PSSM_DEBUG_RENDER + lightColorOut = debugColor; + #endif + + OUT_col = AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/lightingUtils.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/lightingUtils.hlsl new file mode 100644 index 000000000..2bff18999 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/lightingUtils.hlsl @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +float attenuate( float4 lightColor, float2 attParams, float dist ) +{ + // We're summing the results of a scaled constant, + // linear, and quadratic attenuation. + + #ifdef ACCUMULATE_LUV + return lightColor.w * ( 1.0 - dot( attParams, float2( dist, dist * dist ) ) ); + #else + return 1.0 - dot( attParams, float2( dist, dist * dist ) ); + #endif +} + +float3 getDistanceVectorToPlane( float3 origin, float3 direction, float4 plane ) +{ + float denum = dot( plane.xyz, direction.xyz ); + float num = dot( plane, float4( origin, 1.0 ) ); + float t = -num / denum; + + return direction.xyz * t; +} + +float3 getDistanceVectorToPlane( float negFarPlaneDotEye, float3 direction, float4 plane ) +{ + float denum = dot( plane.xyz, direction.xyz ); + float t = negFarPlaneDotEye / denum; + + return direction.xyz * t; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightP.hlsl new file mode 100644 index 000000000..7ff5d50d2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightP.hlsl @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "farFrustumQuad.hlsl" +#include "lightingUtils.hlsl" +#include "../../lighting.hlsl" +#include "../../shaderModel.hlsl" +#include "../../shaderModelAutoGen.hlsl" + + +struct ConvexConnectP +{ + float4 pos : TORQUE_POSITION; + float4 ssPos : TEXCOORD0; + float3 vsEyeDir : TEXCOORD1; +}; + +TORQUE_UNIFORM_SAMPLER2D(prePassBuffer, 0); + +uniform float4 lightPosition; +uniform float4 lightColor; +uniform float lightRange; +uniform float4 vsFarPlane; +uniform float4 rtParams0; + +float4 main( ConvexConnectP IN ) : TORQUE_TARGET0 +{ + // Compute scene UV + float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; + float2 uvScene = getUVFromSSPos(ssPos, rtParams0); + + // Sample/unpack the normal/z data + float4 prepassSample = TORQUE_PREPASS_UNCONDITION(prePassBuffer, uvScene); + float3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Eye ray - Eye -> Pixel + float3 eyeRay = getDistanceVectorToPlane(-vsFarPlane.w, IN.vsEyeDir, vsFarPlane); + float3 viewSpacePos = eyeRay * depth; + + // Build light vec, get length, clip pixel if needed + float3 lightVec = lightPosition.xyz - viewSpacePos; + float lenLightV = length(lightVec); + clip(lightRange - lenLightV); + + // Do a very simple falloff instead of real attenuation + float atten = 1.0 - saturate(lenLightV / lightRange); + + // Normalize lightVec + lightVec /= lenLightV; + + // N.L * Attenuation + float Sat_NL_Att = saturate(dot(lightVec, normal)) * atten; + + // Output, no specular + return lightinfoCondition(lightColor.rgb, Sat_NL_Att, 0.0, 0.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightV.hlsl new file mode 100644 index 000000000..faa2ec115 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/particlePointLightV.hlsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../hlslStructs.hlsl" +#include "../../shaderModel.hlsl" + +struct ConvexConnectV +{ + float4 hpos : TORQUE_POSITION; + float4 ssPos : TEXCOORD0; + float3 vsEyeDir : TEXCOORD1; +}; + +uniform float4x4 viewProj; +uniform float4x4 view; +uniform float3 particlePosWorld; +uniform float lightRange; + +ConvexConnectV main( VertexIn_P IN ) +{ + ConvexConnectV OUT; + float4 pos = float4(IN.pos, 0.0); + float4 vPosWorld = pos + float4(particlePosWorld, 0.0) + pos * lightRange; + OUT.hpos = mul(viewProj, vPosWorld); + OUT.vsEyeDir = mul(view, vPosWorld); + OUT.ssPos = OUT.hpos; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/pointLightP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/pointLightP.hlsl new file mode 100644 index 000000000..a8c0ea105 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/pointLightP.hlsl @@ -0,0 +1,277 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModelAutoGen.hlsl" + +#include "farFrustumQuad.hlsl" +#include "lightingUtils.hlsl" +#include "../../lighting.hlsl" +#include "../shadowMap/shadowMapIO_HLSL.h" +#include "softShadow.hlsl" +#include "../../torque.hlsl" + +struct ConvexConnectP +{ + float4 pos : TORQUE_POSITION; + float4 wsEyeDir : TEXCOORD0; + float4 ssPos : TEXCOORD1; + float4 vsEyeDir : TEXCOORD2; +}; + + +#ifdef USE_COOKIE_TEX + +/// The texture for cookie rendering. +TORQUE_UNIFORM_SAMPLERCUBE(cookieMap, 3); + +#endif + + +#ifdef SHADOW_CUBE + + float3 decodeShadowCoord( float3 shadowCoord ) + { + return shadowCoord; + } + + float4 shadowSample( TORQUE_SAMPLERCUBE(shadowMap), float3 shadowCoord ) + { + return TORQUE_TEXCUBE( shadowMap, shadowCoord ); + } + +#else + + float3 decodeShadowCoord( float3 paraVec ) + { + // Flip y and z + paraVec = paraVec.xzy; + + #ifndef SHADOW_PARABOLOID + + bool calcBack = (paraVec.z < 0.0); + if ( calcBack ) + { + paraVec.z = paraVec.z * -1.0; + + #ifdef SHADOW_DUALPARABOLOID + paraVec.x = -paraVec.x; + #endif + } + + #endif + + float3 shadowCoord; + shadowCoord.x = (paraVec.x / (2*(1 + paraVec.z))) + 0.5; + shadowCoord.y = 1-((paraVec.y / (2*(1 + paraVec.z))) + 0.5); + shadowCoord.z = 0; + + // adjust the co-ordinate slightly if it is near the extent of the paraboloid + // this value was found via experementation + // NOTE: this is wrong, it only biases in one direction, not towards the uv + // center ( 0.5 0.5 ). + //shadowCoord.xy *= 0.997; + + #ifndef SHADOW_PARABOLOID + + // If this is the back, offset in the atlas + if ( calcBack ) + shadowCoord.x += 1.0; + + // Atlasing front and back maps, so scale + shadowCoord.x *= 0.5; + + #endif + + return shadowCoord; + } + +#endif + +TORQUE_UNIFORM_SAMPLER2D(prePassBuffer, 0); + +#ifdef SHADOW_CUBE +TORQUE_UNIFORM_SAMPLERCUBE(shadowMap, 1); +#else +TORQUE_UNIFORM_SAMPLER2D(shadowMap, 1); +TORQUE_UNIFORM_SAMPLER2D(dynamicShadowMap, 2); +#endif + +TORQUE_UNIFORM_SAMPLER2D(lightBuffer, 5); +TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 6); +TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 7); + +uniform float4 rtParams0; +uniform float4 lightColor; + +uniform float lightBrightness; +uniform float3 lightPosition; + +uniform float4 lightMapParams; +uniform float4 vsFarPlane; +uniform float4 lightParams; + +uniform float lightRange; +uniform float shadowSoftness; +uniform float2 lightAttenuation; + +uniform float3x3 viewToLightProj; +uniform float3x3 dynamicViewToLightProj; + +float4 main( ConvexConnectP IN ) : TORQUE_TARGET0 +{ + // Compute scene UV + float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; + float2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); + + // Emissive. + float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + float4 colorSample = TORQUE_TEX2D( colorBuffer, uvScene ); + float3 subsurface = float3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = float3(0.772549, 0.337255, 0.262745); + else + subsurface = float3(0.337255, 0.772549, 0.262745); + } + + // Sample/unpack the normal/z data + float4 prepassSample = TORQUE_PREPASS_UNCONDITION( prePassBuffer, uvScene ); + float3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Eye ray - Eye -> Pixel + float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane ); + float3 viewSpacePos = eyeRay * depth; + + // Build light vec, get length, clip pixel if needed + float3 lightVec = lightPosition - viewSpacePos; + float lenLightV = length( lightVec ); + clip( lightRange - lenLightV ); + + // Get the attenuated falloff. + float atten = attenuate( lightColor, lightAttenuation, lenLightV ); + clip( atten - 1e-6 ); + + // Normalize lightVec + lightVec /= lenLightV; + + // If we can do dynamic branching then avoid wasting + // fillrate on pixels that are backfacing to the light. + float nDotL = dot( lightVec, normal ); + //DB_CLIP( nDotL < 0 ); + + #ifdef NO_SHADOW + + float shadowed = 1.0; + + #else + + // Get a linear depth from the light source. + float distToLight = lenLightV / lightRange; + + #ifdef SHADOW_CUBE + + // TODO: We need to fix shadow cube to handle soft shadows! + float occ = TORQUE_TEXCUBE( shadowMap, mul( viewToLightProj, -lightVec ) ).r; + float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) ); + + #else + + // Static + float2 shadowCoord = decodeShadowCoord( mul( viewToLightProj, -lightVec ) ).xy; + float static_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(shadowMap), + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + // Dynamic + float2 dynamicShadowCoord = decodeShadowCoord( mul( dynamicViewToLightProj, -lightVec ) ).xy; + float dynamic_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap), + ssPos.xy, + dynamicShadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + float shadowed = min(static_shadowed, dynamic_shadowed); + + #endif + + #endif // !NO_SHADOW + + float3 lightcol = lightColor.rgb; + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + float4 cookie = TORQUE_TEXCUBE( cookieMap, mul( viewToLightProj, -lightVec ) ); + + // Multiply the light with the cookie tex. + lightcol *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + + // NOTE: Do not clip on fully shadowed pixels as it would + // cause the hardware occlusion query to disable the shadow. + + // Specular term + float specular = AL_CalcSpecular( lightVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; + + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + float3 lightColorOut = lightMapParams.rgb * lightcol; + float4 addToResult = 0.0; + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = lerp( 1.0f, shadowed, atten ); + lightColorOut = shadowed; + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + return AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/softShadow.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/softShadow.hlsl new file mode 100644 index 000000000..0faf3e1fb --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/softShadow.hlsl @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +#if defined( SOFTSHADOW ) && defined( SOFTSHADOW_HIGH_QUALITY ) + +#define NUM_PRE_TAPS 4 +#define NUM_TAPS 12 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +static float2 sNonUniformTaps[NUM_TAPS] = +{ + // These first 4 taps are located around the edges + // of the disk and are used to predict fully shadowed + // or unshadowed areas. + { 0.992833, 0.979309 }, + { -0.998585, 0.985853 }, + { 0.949299, -0.882562 }, + { -0.941358, -0.893924 }, + + // The rest of the samples. + { 0.545055, -0.589072 }, + { 0.346526, 0.385821 }, + { -0.260183, 0.334412 }, + { 0.248676, -0.679605 }, + { -0.569502, -0.390637 }, + { -0.614096, 0.212577 }, + { -0.259178, 0.876272 }, + { 0.649526, 0.864333 }, +}; + +#else + +#define NUM_PRE_TAPS 5 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +static float2 sNonUniformTaps[NUM_PRE_TAPS] = +{ + { 0.892833, 0.959309 }, + { -0.941358, -0.873924 }, + { -0.260183, 0.334412 }, + { 0.348676, -0.679605 }, + { -0.569502, -0.390637 }, +}; + +#endif + + +/// The texture used to do per-pixel pseudorandom +/// rotations of the filter taps. +TORQUE_UNIFORM_SAMPLER2D(gTapRotationTex, 4); + +float softShadow_sampleTaps( TORQUE_SAMPLER2D(shadowMap1), + float2 sinCos, + float2 shadowPos, + float filterRadius, + float distToLight, + float esmFactor, + int startTap, + int endTap ) +{ + float shadow = 0; + + float2 tap = 0; + for ( int t = startTap; t < endTap; t++ ) + { + tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; + tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; + float occluder = TORQUE_TEX2DLOD( shadowMap1, float4( shadowPos + tap, 0, 0 ) ).r; + + float esm = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + shadow += esm / float( endTap - startTap ); + } + + return shadow; +} + + +float softShadow_filter( TORQUE_SAMPLER2D(shadowMap), + float2 vpos, + float2 shadowPos, + float filterRadius, + float distToLight, + float dotNL, + float esmFactor ) +{ + #ifndef SOFTSHADOW + + // If softshadow is undefined then we skip any complex + // filtering... just do a single sample ESM. + + float occluder = TORQUE_TEX2DLOD(shadowMap, float4(shadowPos, 0, 0)).r; + float shadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + + #else + // Lookup the random rotation for this screen pixel. + float2 sinCos = ( TORQUE_TEX2DLOD(gTapRotationTex, float4(vpos * 16, 0, 0)).rg - 0.5) * 2; + + // Do the prediction taps first. + float shadow = softShadow_sampleTaps( TORQUE_SAMPLER2D_MAKEARG(shadowMap), + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + 0, + NUM_PRE_TAPS ); + + // We live with only the pretap results if we don't + // have high quality shadow filtering enabled. + #ifdef SOFTSHADOW_HIGH_QUALITY + + // Only do the expensive filtering if we're really + // in a partially shadowed area. + if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0 ) > 0.06 ) + { + shadow += softShadow_sampleTaps( TORQUE_SAMPLER2D_MAKEARG(shadowMap), + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + NUM_PRE_TAPS, + NUM_TAPS ); + + // This averages the taps above with the results + // of the prediction samples. + shadow *= 0.5; + } + + #endif // SOFTSHADOW_HIGH_QUALITY + + #endif // SOFTSHADOW + + return shadow; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/spotLightP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/spotLightP.hlsl new file mode 100644 index 000000000..5040b15e2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/spotLightP.hlsl @@ -0,0 +1,209 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +#include "farFrustumQuad.hlsl" +#include "lightingUtils.hlsl" +#include "../../lighting.hlsl" +#include "../shadowMap/shadowMapIO_HLSL.h" +#include "softShadow.hlsl" +#include "../../torque.hlsl" + +struct ConvexConnectP +{ + float4 pos : TORQUE_POSITION; + float4 wsEyeDir : TEXCOORD0; + float4 ssPos : TEXCOORD1; + float4 vsEyeDir : TEXCOORD2; +}; + +TORQUE_UNIFORM_SAMPLER2D(prePassBuffer, 0); +TORQUE_UNIFORM_SAMPLER2D(shadowMap, 1); +TORQUE_UNIFORM_SAMPLER2D(dynamicShadowMap,2); + +#ifdef USE_COOKIE_TEX + +/// The texture for cookie rendering. +TORQUE_UNIFORM_SAMPLER2D(cookieMap, 3); + +#endif + +TORQUE_UNIFORM_SAMPLER2D(lightBuffer, 5); +TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 6); +TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 7); + +uniform float4 rtParams0; + +uniform float lightBrightness; +uniform float3 lightPosition; + +uniform float4 lightColor; + +uniform float lightRange; +uniform float3 lightDirection; + +uniform float4 lightSpotParams; +uniform float4 lightMapParams; +uniform float4 vsFarPlane; +uniform float4x4 viewToLightProj; +uniform float4 lightParams; +uniform float4x4 dynamicViewToLightProj; + +uniform float2 lightAttenuation; +uniform float shadowSoftness; + +float4 main( ConvexConnectP IN ) : TORQUE_TARGET0 +{ + // Compute scene UV + float3 ssPos = IN.ssPos.xyz / IN.ssPos.w; + float2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); + + // Emissive. + float4 matInfo = TORQUE_TEX2D( matInfoBuffer, uvScene ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + return float4(0.0, 0.0, 0.0, 0.0); + } + + float4 colorSample = TORQUE_TEX2D( colorBuffer, uvScene ); + float3 subsurface = float3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = float3(0.772549, 0.337255, 0.262745); + else + subsurface = float3(0.337255, 0.772549, 0.262745); + } + + // Sample/unpack the normal/z data + float4 prepassSample = TORQUE_PREPASS_UNCONDITION( prePassBuffer, uvScene ); + float3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Eye ray - Eye -> Pixel + float3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN.vsEyeDir.xyz, vsFarPlane ); + float3 viewSpacePos = eyeRay * depth; + + // Build light vec, get length, clip pixel if needed + float3 lightToPxlVec = viewSpacePos - lightPosition; + float lenLightV = length( lightToPxlVec ); + lightToPxlVec /= lenLightV; + + //lightDirection = float3( -lightDirection.xy, lightDirection.z ); //float3( 0, 0, -1 ); + float cosAlpha = dot( lightDirection, lightToPxlVec ); + clip( cosAlpha - lightSpotParams.x ); + clip( lightRange - lenLightV ); + + float atten = attenuate( lightColor, lightAttenuation, lenLightV ); + atten *= ( cosAlpha - lightSpotParams.x ) / lightSpotParams.y; + clip( atten - 1e-6 ); + atten = saturate( atten ); + + float nDotL = dot( normal, -lightToPxlVec ); + + // Get the shadow texture coordinate + float4 pxlPosLightProj = mul( viewToLightProj, float4( viewSpacePos, 1 ) ); + float2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + float2( 0.5, 0.5 ); + shadowCoord.y = 1.0f - shadowCoord.y; + + // Get the dynamic shadow texture coordinate + float4 dynpxlPosLightProj = mul( dynamicViewToLightProj, float4( viewSpacePos, 1 ) ); + float2 dynshadowCoord = ( ( dynpxlPosLightProj.xy / dynpxlPosLightProj.w ) * 0.5 ) + float2( 0.5, 0.5 ); + dynshadowCoord.y = 1.0f - dynshadowCoord.y; + + #ifdef NO_SHADOW + + float shadowed = 1.0; + + #else + + // Get a linear depth from the light source. + float distToLight = pxlPosLightProj.z / lightRange; + + float static_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(shadowMap), + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + + float dynamic_shadowed = softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap), + ssPos.xy, + dynshadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); + float shadowed = min(static_shadowed, dynamic_shadowed); + #endif // !NO_SHADOW + + float3 lightcol = lightColor.rgb; + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + float4 cookie = TORQUE_TEX2D( cookieMap, shadowCoord ); + + // Multiply the light with the cookie tex. + lightcol *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + + // NOTE: Do not clip on fully shadowed pixels as it would + // cause the hardware occlusion query to disable the shadow. + + // Specular term + float specular = AL_CalcSpecular( -lightToPxlVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; + + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + float3 lightColorOut = lightMapParams.rgb * lightcol; + float4 addToResult = 0.0; + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = lerp( 1.0f, shadowed, atten ); + lightColorOut = shadowed; + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + return AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/advanced/vectorLightP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/vectorLightP.hlsl new file mode 100644 index 000000000..956227909 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/advanced/vectorLightP.hlsl @@ -0,0 +1,328 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +#include "farFrustumQuad.hlsl" +#include "../../torque.hlsl" +#include "../../lighting.hlsl" +#include "lightingUtils.hlsl" +#include "../shadowMap/shadowMapIO_HLSL.h" +#include "softShadow.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prePassBuffer, 0); +TORQUE_UNIFORM_SAMPLER2D(shadowMap, 1); +TORQUE_UNIFORM_SAMPLER2D(dynamicShadowMap, 2); + +#ifdef USE_SSAO_MASK +TORQUE_UNIFORM_SAMPLER2D(ssaoMask, 3); +uniform float4 rtParams3; +#endif +//register 4? +TORQUE_UNIFORM_SAMPLER2D(lightBuffer, 5); +TORQUE_UNIFORM_SAMPLER2D(colorBuffer, 6); +TORQUE_UNIFORM_SAMPLER2D(matInfoBuffer, 7); + +uniform float lightBrightness; +uniform float3 lightDirection; + +uniform float4 lightColor; +uniform float4 lightAmbient; + +uniform float shadowSoftness; +uniform float3 eyePosWorld; + +uniform float4 atlasXOffset; +uniform float4 atlasYOffset; +uniform float4 zNearFarInvNearFar; +uniform float4 lightMapParams; +uniform float4 farPlaneScalePSSM; +uniform float4 overDarkPSSM; + +uniform float2 fadeStartLength; +uniform float2 atlasScale; + +uniform float4x4 eyeMat; + +// Static Shadows +uniform float4x4 worldToLightProj; +uniform float4 scaleX; +uniform float4 scaleY; +uniform float4 offsetX; +uniform float4 offsetY; +// Dynamic Shadows +uniform float4x4 dynamicWorldToLightProj; +uniform float4 dynamicScaleX; +uniform float4 dynamicScaleY; +uniform float4 dynamicOffsetX; +uniform float4 dynamicOffsetY; +uniform float4 dynamicFarPlaneScalePSSM; + +float4 AL_VectorLightShadowCast( TORQUE_SAMPLER2D(sourceShadowMap), + float2 texCoord, + float4x4 worldToLightProj, + float4 worldPos, + float4 scaleX, + float4 scaleY, + float4 offsetX, + float4 offsetY, + float4 farPlaneScalePSSM, + float4 atlasXOffset, + float4 atlasYOffset, + float2 atlasScale, + float shadowSoftness, + float dotNL , + float4 overDarkPSSM) +{ + // Compute shadow map coordinate + float4 pxlPosLightProj = mul(worldToLightProj, worldPos); + float2 baseShadowCoord = pxlPosLightProj.xy / pxlPosLightProj.w; + + // Distance to light, in shadowmap space + float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; + + // Figure out which split to sample from. Basically, we compute the shadowmap sample coord + // for all of the splits and then check if its valid. + float4 shadowCoordX = baseShadowCoord.xxxx; + float4 shadowCoordY = baseShadowCoord.yyyy; + float4 farPlaneDists = distToLight.xxxx; + shadowCoordX *= scaleX; + shadowCoordY *= scaleY; + shadowCoordX += offsetX; + shadowCoordY += offsetY; + farPlaneDists *= farPlaneScalePSSM; + + // If the shadow sample is within -1..1 and the distance + // to the light for this pixel is less than the far plane + // of the split, use it. + float4 finalMask; + if ( shadowCoordX.x > -0.99 && shadowCoordX.x < 0.99 && + shadowCoordY.x > -0.99 && shadowCoordY.x < 0.99 && + farPlaneDists.x < 1.0 ) + finalMask = float4(1, 0, 0, 0); + + else if ( shadowCoordX.y > -0.99 && shadowCoordX.y < 0.99 && + shadowCoordY.y > -0.99 && shadowCoordY.y < 0.99 && + farPlaneDists.y < 1.0 ) + finalMask = float4(0, 1, 0, 0); + + else if ( shadowCoordX.z > -0.99 && shadowCoordX.z < 0.99 && + shadowCoordY.z > -0.99 && shadowCoordY.z < 0.99 && + farPlaneDists.z < 1.0 ) + finalMask = float4(0, 0, 1, 0); + + else + finalMask = float4(0, 0, 0, 1); + + float3 debugColor = float3(0,0,0); + + #ifdef NO_SHADOW + debugColor = float3(1.0,1.0,1.0); + #endif + + #ifdef PSSM_DEBUG_RENDER + if ( finalMask.x > 0 ) + debugColor += float3( 1, 0, 0 ); + else if ( finalMask.y > 0 ) + debugColor += float3( 0, 1, 0 ); + else if ( finalMask.z > 0 ) + debugColor += float3( 0, 0, 1 ); + else if ( finalMask.w > 0 ) + debugColor += float3( 1, 1, 0 ); + #endif + + // Here we know what split we're sampling from, so recompute the texcoord location + // Yes, we could just use the result from above, but doing it this way actually saves + // shader instructions. + float2 finalScale; + finalScale.x = dot(finalMask, scaleX); + finalScale.y = dot(finalMask, scaleY); + + float2 finalOffset; + finalOffset.x = dot(finalMask, offsetX); + finalOffset.y = dot(finalMask, offsetY); + + float2 shadowCoord; + shadowCoord = baseShadowCoord * finalScale; + shadowCoord += finalOffset; + + // Convert to texcoord space + shadowCoord = 0.5 * shadowCoord + float2(0.5, 0.5); + shadowCoord.y = 1.0f - shadowCoord.y; + + // Move around inside of atlas + float2 aOffset; + aOffset.x = dot(finalMask, atlasXOffset); + aOffset.y = dot(finalMask, atlasYOffset); + + shadowCoord *= atlasScale; + shadowCoord += aOffset; + + // Each split has a different far plane, take this into account. + float farPlaneScale = dot( farPlaneScalePSSM, finalMask ); + distToLight *= farPlaneScale; + + return float4(debugColor, + softShadow_filter( TORQUE_SAMPLER2D_MAKEARG(sourceShadowMap), + texCoord, + shadowCoord, + farPlaneScale * shadowSoftness, + distToLight, + dotNL, + dot( finalMask, overDarkPSSM ) ) ); +}; + +float4 main( FarFrustumQuadConnectP IN ) : TORQUE_TARGET0 +{ + // Emissive. + float4 matInfo = TORQUE_TEX2D( matInfoBuffer, IN.uv0 ); + bool emissive = getFlag( matInfo.r, 0 ); + if ( emissive ) + { + return float4(1.0, 1.0, 1.0, 0.0); + } + + float4 colorSample = TORQUE_TEX2D( colorBuffer, IN.uv0 ); + float3 subsurface = float3(0.0,0.0,0.0); + if (getFlag( matInfo.r, 1 )) + { + subsurface = colorSample.rgb; + if (colorSample.r>colorSample.g) + subsurface = float3(0.772549, 0.337255, 0.262745); + else + subsurface = float3(0.337255, 0.772549, 0.262745); + } + // Sample/unpack the normal/z data + float4 prepassSample = TORQUE_PREPASS_UNCONDITION( prePassBuffer, IN.uv0 ); + float3 normal = prepassSample.rgb; + float depth = prepassSample.a; + + // Use eye ray to get ws pos + float4 worldPos = float4(eyePosWorld + IN.wsEyeRay * depth, 1.0f); + + // Get the light attenuation. + float dotNL = dot(-lightDirection, normal); + + #ifdef PSSM_DEBUG_RENDER + float3 debugColor = float3(0,0,0); + #endif + + #ifdef NO_SHADOW + + // Fully unshadowed. + float shadowed = 1.0; + + #ifdef PSSM_DEBUG_RENDER + debugColor = float3(1.0,1.0,1.0); + #endif + + #else + + float4 static_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(shadowMap), + IN.uv0.xy, + worldToLightProj, + worldPos, + scaleX, scaleY, + offsetX, offsetY, + farPlaneScalePSSM, + atlasXOffset, atlasYOffset, + atlasScale, + shadowSoftness, + dotNL, + overDarkPSSM); + float4 dynamic_shadowed_colors = AL_VectorLightShadowCast( TORQUE_SAMPLER2D_MAKEARG(dynamicShadowMap), + IN.uv0.xy, + dynamicWorldToLightProj, + worldPos, + dynamicScaleX, dynamicScaleY, + dynamicOffsetX, dynamicOffsetY, + dynamicFarPlaneScalePSSM, + atlasXOffset, atlasYOffset, + atlasScale, + shadowSoftness, + dotNL, + overDarkPSSM); + + float static_shadowed = static_shadowed_colors.a; + float dynamic_shadowed = dynamic_shadowed_colors.a; + + #ifdef PSSM_DEBUG_RENDER + debugColor = static_shadowed_colors.rgb*0.5+dynamic_shadowed_colors.rgb*0.5; + #endif + + // Fade out the shadow at the end of the range. + float4 zDist = (zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); + float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y; + + static_shadowed = lerp( static_shadowed, 1.0, saturate( fadeOutAmt ) ); + dynamic_shadowed = lerp( dynamic_shadowed, 1.0, saturate( fadeOutAmt ) ); + + // temp for debugging. uncomment one or the other. + //float shadowed = static_shadowed; + //float shadowed = dynamic_shadowed; + float shadowed = min(static_shadowed, dynamic_shadowed); + + #ifdef PSSM_DEBUG_RENDER + if ( fadeOutAmt > 1.0 ) + debugColor = 1.0; + #endif + + #endif // !NO_SHADOW + + // Specular term + float specular = AL_CalcSpecular( -lightDirection, + normal, + normalize(-IN.vsEyeRay) ) * lightBrightness * shadowed; + + float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness; + float3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + + float4 addToResult = (lightAmbient * (1 - ambientCameraFactor)) + ( lightAmbient * ambientCameraFactor * saturate(dot(normalize(-IN.vsEyeRay), normal)) ); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = dotNL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + lightColorOut = shadowed; + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + // Sample the AO texture. + #ifdef USE_SSAO_MASK + float ao = 1.0 - TORQUE_TEX2D( ssaoMask, viewportCoordToRenderTarget( IN.uv0.xy, rtParams3 ) ).r; + addToResult *= ao; + #endif + + #ifdef PSSM_DEBUG_RENDER + lightColorOut = debugColor; + #endif + + return AL_DeferredOutput(lightColorOut+subsurface*(1.0-Sat_NL_Att), colorSample.rgb, matInfo, addToResult, specular, Sat_NL_Att); +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterP.glsl new file mode 100644 index 000000000..9b510e0cf --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterP.glsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +uniform sampler2D diffuseMap; + +in vec2 uv; + +uniform vec2 oneOverTargetSize; + +const float offset[3] = float[]( 0.0, 1.3846153846, 3.2307692308 ); +const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 ); + +out vec4 OUT_col; + +void main() +{ + OUT_col = texture( diffuseMap, uv ) * weight[0]; + + for ( int i=1; i < 3; i++ ) + { + vec2 _sample = (BLUR_DIR * offset[i]) * oneOverTargetSize; + OUT_col += texture( diffuseMap, uv + _sample ) * weight[i]; + OUT_col += texture( diffuseMap, uv - _sample ) * weight[i]; + } +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterV.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterV.glsl new file mode 100644 index 000000000..0eeb2e0fd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/basic/gl/shadowFilterV.glsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../../../../shaders/common/gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform vec4 rtParams0; + +out vec2 uv; + +void main() +{ + gl_Position = vPosition; + uv = viewportCoordToRenderTarget( vTexCoord0.st, rtParams0 ); + gl_Position.y *= -1; //correct ssp +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterP.hlsl new file mode 100644 index 000000000..b56aade8d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterP.hlsl @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaders/common/postFx/postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + float2 uv : TEXCOORD0; +}; + +static float offset[3] = { 0.0, 1.3846153846, 3.2307692308 }; +static float weight[3] = { 0.2270270270, 0.3162162162, 0.0702702703 }; + +uniform float2 oneOverTargetSize; + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ + float4 OUT = TORQUE_TEX2D( diffuseMap, IN.uv ) * weight[0]; + + for ( int i=1; i < 3; i++ ) + { + float2 sample = (BLUR_DIR * offset[i]) * oneOverTargetSize; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv + sample ) * weight[i]; + OUT += TORQUE_TEX2D(diffuseMap, IN.uv - sample) * weight[i]; + } + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterV.hlsl new file mode 100644 index 000000000..c89af7357 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/basic/shadowFilterV.hlsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../../../../shaders/common/postFx/postFx.hlsl" +#include "../../../../../../shaders/common/torque.hlsl" + +float4 rtParams0; + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + float2 uv : TEXCOORD0; +}; + +VertToPix main( PFXVert IN ) +{ + VertToPix OUT; + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterP.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterP.hlsl new file mode 100644 index 000000000..a187c3c63 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterP.hlsl @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//***************************************************************************** +// Box Filter +//***************************************************************************** +#include "../ShaderModel.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 tex0 : TEXCOORD0; +}; + +// If not defined from ShaderData then define +// the default blur kernel size here. +//#ifndef blurSamples +// #define blurSamples 4 +//#endif + +float log_conv ( float x0, float X, float y0, float Y ) +{ + return (X + log(x0 + (y0 * exp(Y - X)))); +} + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap0, 0); +uniform float texSize : register(C0); +uniform float2 blurDimension : register(C2); +uniform float2 blurBoundaries : register(C3); + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + // 5x5 + if (IN.tex0.x <= blurBoundaries.x) + { + float texelSize = 1.2f / texSize; + float2 sampleOffset = texelSize * blurDimension; + //float2 offset = 0.5 * float( blurSamples ) * sampleOffset; + + float2 texCoord = IN.tex0; + + float accum = log_conv(0.3125, TORQUE_TEX2D(diffuseMap0, texCoord - sampleOffset), 0.375, tex2D(diffuseMap0, texCoord)); + accum = log_conv(1, accum, 0.3125, TORQUE_TEX2D(diffuseMap0, texCoord + sampleOffset)); + + return accum; + } else { + // 3x3 + if (IN.tex0.x <= blurBoundaries.y) + { + float texelSize = 1.3f / texSize; + float2 sampleOffset = texelSize * blurDimension; + //float2 offset = 0.5 * float( blurSamples ) * sampleOffset; + + float2 texCoord = IN.tex0; + float accum = log_conv(0.5, tex2D(diffuseMap0, texCoord - sampleOffset), 0.5, tex2D(diffuseMap0, texCoord + sampleOffset)); + + return accum; + } else { + return TORQUE_TEX2D(diffuseMap0, IN.tex0); + } + } +} + diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterV.hlsl b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterV.hlsl new file mode 100644 index 000000000..3679e41bb --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/boxFilterV.hlsl @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//***************************************************************************** +// Box Filter +//***************************************************************************** +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +#include "../ShaderModel.hlsl" + +struct VertData +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 tex0 : TEXCOORD0; +}; + + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertData IN, + uniform float4x4 modelview : register(C0)) +{ + ConnectData OUT; + + OUT.hpos = mul(modelview, float4(IN.position,1.0)); + OUT.tex0 = IN.texCoord; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl new file mode 100644 index 000000000..d4e05132b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define blurSamples 4.0 + +uniform sampler2D diffuseMap0; +uniform float texSize; +uniform vec2 blurDimension; + +in vec2 tex0; + +out vec4 OUT_col; + +void main() +{ + // Preshader + float TexelSize = 1.0 / texSize; + vec2 SampleOffset = TexelSize * blurDimension; + vec2 Offset = 0.5 * float(blurSamples - 1.0) * SampleOffset; + + vec2 BaseTexCoord = tex0 - Offset; + + vec4 accum = vec4(0.0, 0.0, 0.0, 0.0); + for(int i = 0; i < int(blurSamples); i++) + { + accum += texture(diffuseMap0, BaseTexCoord + float(i) * SampleOffset); + } + accum /= blurSamples; + OUT_col = accum; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl new file mode 100644 index 000000000..9fc436f6c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform mat4 modelview; + +out vec2 tex0; + +void main() +{ + gl_Position = modelview * vPosition; + tex0 = vTexCoord0.st; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO.h b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO.h new file mode 100644 index 000000000..84ef6b6a8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//#define SM_Fmt_R8G8B8A8 + +#define pkDepthBitShft 65536.0 +#define pkDepthChanMax 256.0 +#define bias -0.5/255.0 +#define coeff 0.9999991 +//#define coeff 1.0 + +float4 encodeShadowMap( float depth ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return frac( float4(1.0, 255.0, 65025.0, 160581375.0) * depth ) + bias; + + //float4 packedValue = frac((depth / coeff) * float4(16777216.0, 65536.0, 256.0, 1.0)); + //return (packedValue - packedValue.xxyz * float4(0, 1.0 / 256, 1.0 / 256, 1.0 / 256)); +#else + return depth; +#endif +} + +float decodeShadowMap( float4 smSample ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return dot( smSample, float4(1.0, 1/255.0, 1/65025.0, 1/160581375.0) ); +#else + return smSample.x; +#endif +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_GLSL.h b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_GLSL.h new file mode 100644 index 000000000..10d69b834 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_GLSL.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//#define SM_Fmt_R8G8B8A8 + +#define pkDepthBitShft 65536.0 +#define pkDepthChanMax 256.0 +#define bias -0.5/255.0 +#define coeff 0.9999991 +//#define coeff 1.0 + +vec4 encodeShadowMap( float depth ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return frac( vec4(1.0, 255.0, 65025.0, 160581375.0) * depth ) + vec4(bias); + + //float4 packedValue = frac((depth / coeff) * float4(16777216.0, 65536.0, 256.0, 1.0)); + //return (packedValue - packedValue.xxyz * float4(0, 1.0 / 256, 1.0 / 256, 1.0 / 256)); +#else + return vec4(depth); +#endif +} + +float decodeShadowMap( vec4 smSample ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return dot( smSample, vec4(1.0, 1/255.0, 1/65025.0, 1/160581375.0) ); +#else + return smSample.x; +#endif +} diff --git a/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_HLSL.h b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_HLSL.h new file mode 100644 index 000000000..84ef6b6a8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/lighting/shadowMap/shadowMapIO_HLSL.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//#define SM_Fmt_R8G8B8A8 + +#define pkDepthBitShft 65536.0 +#define pkDepthChanMax 256.0 +#define bias -0.5/255.0 +#define coeff 0.9999991 +//#define coeff 1.0 + +float4 encodeShadowMap( float depth ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return frac( float4(1.0, 255.0, 65025.0, 160581375.0) * depth ) + bias; + + //float4 packedValue = frac((depth / coeff) * float4(16777216.0, 65536.0, 256.0, 1.0)); + //return (packedValue - packedValue.xxyz * float4(0, 1.0 / 256, 1.0 / 256, 1.0 / 256)); +#else + return depth; +#endif +} + +float decodeShadowMap( float4 smSample ) +{ +#if defined(SM_Fmt_R8G8B8A8) + return dot( smSample, float4(1.0, 1/255.0, 1/65025.0, 1/160581375.0) ); +#else + return smSample.x; +#endif +} diff --git a/Templates/BaseGame/game/data/shaders/common/particleCompositeP.hlsl b/Templates/BaseGame/game/data/shaders/common/particleCompositeP.hlsl new file mode 100644 index 000000000..6e26ddbdb --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/particleCompositeP.hlsl @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "torque.hlsl" +#include "shaderModel.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(colorSource, 0); +uniform float4 offscreenTargetParams; + +#ifdef TORQUE_LINEAR_DEPTH +#define REJECT_EDGES +TORQUE_UNIFORM_SAMPLER2D(edgeSource, 1); +uniform float4 edgeTargetParams; +#endif + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 offscreenPos : TEXCOORD0; + float4 backbufferPos : TEXCOORD1; +}; + + +float4 main(Conn IN) : TORQUE_TARGET0 +{ + // Off-screen particle source screenspace position in XY + // Back-buffer screenspace position in ZW + float4 ssPos = float4(IN.offscreenPos.xy / IN.offscreenPos.w, IN.backbufferPos.xy / IN.backbufferPos.w); + + float4 uvScene = ( ssPos + 1.0 ) / 2.0; + uvScene.yw = 1.0 - uvScene.yw; + uvScene.xy = viewportCoordToRenderTarget(uvScene.xy, offscreenTargetParams); + +#ifdef REJECT_EDGES + // Cut out particles along the edges, this will create the stencil mask + uvScene.zw = viewportCoordToRenderTarget(uvScene.zw, edgeTargetParams); + float edge = TORQUE_TEX2D( edgeSource, uvScene.zw ).r; + clip( -edge ); +#endif + + // Sample offscreen target and return + return TORQUE_TEX2D( colorSource, uvScene.xy ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/particleCompositeV.hlsl b/Templates/BaseGame/game/data/shaders/common/particleCompositeV.hlsl new file mode 100644 index 000000000..c4c51204a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/particleCompositeV.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Vertex +{ + float3 pos : POSITION; + float4 uvCoord : COLOR0; +}; + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 offscreenPos : TEXCOORD0; + float4 backbufferPos : TEXCOORD1; +}; + +uniform float4 screenRect; // point, extent + +Conn main(Vertex IN) +{ + Conn OUT; + + OUT.hpos = float4(IN.uvCoord.xy, 1.0, 1.0); + OUT.hpos.xy *= screenRect.zw; + OUT.hpos.xy += screenRect.xy; + + OUT.backbufferPos = OUT.hpos; + OUT.offscreenPos = OUT.hpos; + + return OUT; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/particlesP.hlsl b/Templates/BaseGame/game/data/shaders/common/particlesP.hlsl new file mode 100644 index 000000000..37439c59a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/particlesP.hlsl @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "torque.hlsl" +#include "shaderModel.hlsl" +// With advanced lighting we get soft particles. +#ifdef TORQUE_LINEAR_DEPTH + #define SOFTPARTICLES +#endif + +#ifdef SOFTPARTICLES + + #include "shaderModelAutoGen.hlsl" + + uniform float oneOverSoftness; + uniform float oneOverFar; + TORQUE_UNIFORM_SAMPLER2D(prepassTex, 1); + //uniform float3 vEye; + uniform float4 prePassTargetParams; +#endif + +#define CLIP_Z // TODO: Make this a proper macro + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 color : TEXCOORD0; + float2 uv0 : TEXCOORD1; + float4 pos : TEXCOORD2; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); +TORQUE_UNIFORM_SAMPLER2D(paraboloidLightMap, 2); + +float4 lmSample( float3 nrm ) +{ + bool calcBack = (nrm.z < 0.0); + if ( calcBack ) + nrm.z = nrm.z * -1.0; + + float2 lmCoord; + lmCoord.x = (nrm.x / (2*(1 + nrm.z))) + 0.5; + lmCoord.y = 1-((nrm.y / (2*(1 + nrm.z))) + 0.5); + + + // If this is the back, offset in the atlas + if ( calcBack ) + lmCoord.x += 1.0; + + // Atlasing front and back maps, so scale + lmCoord.x *= 0.5; + + return TORQUE_TEX2D(paraboloidLightMap, lmCoord); +} + + +uniform float alphaFactor; +uniform float alphaScale; + +float4 main( Conn IN ) : TORQUE_TARGET0 +{ + float softBlend = 1; + + #ifdef SOFTPARTICLES + float2 tc = IN.pos.xy * float2(1.0, -1.0) / IN.pos.w; + tc = viewportCoordToRenderTarget(saturate( ( tc + 1.0 ) * 0.5 ), prePassTargetParams); + + float sceneDepth = TORQUE_PREPASS_UNCONDITION(prepassTex, tc).w; + float depth = IN.pos.w * oneOverFar; + float diff = sceneDepth - depth; + #ifdef CLIP_Z + // If drawing offscreen, this acts as the depth test, since we don't line up with the z-buffer + // When drawing high-res, though, we want to be able to take advantage of hi-z + // so this is #ifdef'd out + //clip(diff); + #endif + softBlend = saturate( diff * oneOverSoftness ); + #endif + + float4 diffuse = TORQUE_TEX2D( diffuseMap, IN.uv0 ); + + //return float4( lmSample(float3(0, 0, -1)).rgb, IN.color.a * diffuse.a * softBlend * alphaScale); + + // Scale output color by the alpha factor (turn LerpAlpha into pre-multiplied alpha) + float3 colorScale = ( alphaFactor < 0.0 ? IN.color.rgb * diffuse.rgb : ( alphaFactor > 0.0 ? IN.color.a * diffuse.a * alphaFactor * softBlend : softBlend ) ); + + return hdrEncode( float4( IN.color.rgb * diffuse.rgb * colorScale, + IN.color.a * diffuse.a * softBlend * alphaScale ) ); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/particlesV.hlsl b/Templates/BaseGame/game/data/shaders/common/particlesV.hlsl new file mode 100644 index 000000000..dbeff0cc2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/particlesV.hlsl @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Vertex +{ + float3 pos : POSITION; + float4 color : COLOR0; + float2 uv0 : TEXCOORD0; +}; + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 color : TEXCOORD0; + float2 uv0 : TEXCOORD1; + float4 pos : TEXCOORD2; +}; + + +uniform float4x4 modelViewProj; +uniform float4x4 fsModelViewProj; + +Conn main( Vertex In ) +{ + Conn Out; + + Out.hpos = mul( modelViewProj, float4(In.pos,1.0) ); + Out.pos = mul(fsModelViewProj, float4(In.pos, 1.0) ); + Out.color = In.color; + Out.uv0 = In.uv0; + + return Out; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/planarReflectBumpP.hlsl b/Templates/BaseGame/game/data/shaders/common/planarReflectBumpP.hlsl new file mode 100644 index 000000000..d18331fb6 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/planarReflectBumpP.hlsl @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; + float4 tex2 : TEXCOORD1; +}; + + +struct Fragout +{ + float4 col : TORQUE_TARGET0; +}; + +TORQUE_UNIFORM_SAMPLER2D(texMap, 0); +TORQUE_UNIFORM_SAMPLER2D(refractMap, 1); +TORQUE_UNIFORM_SAMPLER2D(bumpMap, 2); + + +//----------------------------------------------------------------------------- +// Fade edges of axis for texcoord passed in +//----------------------------------------------------------------------------- +float fadeAxis( float val ) +{ + // Fades from 1.0 to 0.0 when less than 0.1 + float fadeLow = saturate( val * 10.0 ); + + // Fades from 1.0 to 0.0 when greater than 0.9 + float fadeHigh = 1.0 - saturate( (val - 0.9) * 10.0 ); + + return fadeLow * fadeHigh; +} + + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main( ConnectData IN ) +{ + Fragout OUT; + + float3 bumpNorm = TORQUE_TEX2D( bumpMap, IN.tex2 ) * 2.0 - 1.0; + float2 offset = float2( bumpNorm.x, bumpNorm.y ); + float4 texIndex = IN.texCoord; + + // The fadeVal is used to "fade" the distortion at the edges of the screen. + // This is done so it won't sample the reflection texture out-of-bounds and create artifacts + // Note - this can be done more efficiently with a texture lookup + float fadeVal = fadeAxis( texIndex.x / texIndex.w ) * fadeAxis( texIndex.y / texIndex.w ); + + const float distortion = 0.2; + texIndex.xy += offset * distortion * fadeVal; + + float4 reflectColor = TORQUE_TEX2DPROJ( refractMap, texIndex ); + float4 diffuseColor = TORQUE_TEX2D( texMap, IN.tex2 ); + + OUT.col = diffuseColor + reflectColor * diffuseColor.a; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/planarReflectBumpV.hlsl b/Templates/BaseGame/game/data/shaders/common/planarReflectBumpV.hlsl new file mode 100644 index 000000000..d45adb574 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/planarReflectBumpV.hlsl @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "shdrConsts.h" +#include "shaderModel.hlsl" + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + float3 position : POSITION; + float3 normal : NORMAL; + float2 texCoord : TEXCOORD0; + float2 lmCoord : TEXCOORD1; + float3 T : TEXCOORD2; + float3 B : TEXCOORD3; +}; + + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float4 texCoord : TEXCOORD0; + float2 tex2 : TEXCOORD1; +}; + +uniform float4x4 modelview; +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertData IN ) +{ + ConnectData OUT; + OUT.hpos = mul(modelview, float4(IN.position,1.0)); + + float4x4 texGenTest = { 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 }; + + OUT.texCoord = mul( texGenTest, OUT.hpos ); + + OUT.tex2 = IN.texCoord; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/planarReflectP.hlsl b/Templates/BaseGame/game/data/shaders/common/planarReflectP.hlsl new file mode 100644 index 000000000..43b420544 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/planarReflectP.hlsl @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; + float4 tex2 : TEXCOORD1; +}; + + +struct Fragout +{ + float4 col : TORQUE_TARGET0; +}; + +TORQUE_UNIFORM_SAMPLER2D(texMap, 0); +TORQUE_UNIFORM_SAMPLER2D(refractMap, 1); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main( ConnectData IN ) +{ + Fragout OUT; + + float4 diffuseColor = TORQUE_TEX2D( texMap, IN.texCoord ); + float4 reflectColor = TORQUE_TEX2DPROJ(refractMap, IN.tex2); + + OUT.col = diffuseColor + reflectColor * diffuseColor.a; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/planarReflectV.hlsl b/Templates/BaseGame/game/data/shaders/common/planarReflectV.hlsl new file mode 100644 index 000000000..1f2ca9d4f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/planarReflectV.hlsl @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "hlslStructs.hlsl" +#include "shaderModel.hlsl" + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; + float4 tex2 : TEXCOORD1; +}; + +uniform float4x4 modelview; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertexIn_PNTTTB IN ) +{ + ConnectData OUT; + OUT.hpos = mul(modelview, float4(IN.pos,1.0)); + + float4x4 texGenTest = { 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 }; + + OUT.texCoord = IN.uv0; + OUT.tex2 = mul( texGenTest, OUT.hpos ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/VolFogGlowP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/VolFogGlowP.hlsl new file mode 100644 index 000000000..c3adb3b55 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/VolFogGlowP.hlsl @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 R.G.S. - Richards Game Studio, the Netherlands +// http://www.richardsgamestudio.com/ +// +// If you find this code useful or you are feeling particularly generous I +// would ask that you please go to http://www.richardsgamestudio.com/ then +// choose Donations from the menu on the left side and make a donation to +// Richards Game Studio. It will be highly appreciated. +// +// The MIT License: +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog Glow postFx pixel shader V1.00 + +#include "./postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); +uniform float strength; + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ + float4 kernel = float4( 0.175, 0.275, 0.375, 0.475 ) * strength; + + float4 OUT = 0; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv0 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv1 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv2 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv3 ) * kernel.w; + + OUT += TORQUE_TEX2D( diffuseMap, IN.uv4 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv5 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv6 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + float3 rgb2lum = float3( 0.30, 0.59, 0.11 ); + OUT.a = dot( OUT.rgb, rgb2lum ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/caustics/causticsP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/caustics/causticsP.hlsl new file mode 100644 index 000000000..d2f4a058a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/caustics/causticsP.hlsl @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +uniform float accumTime; +uniform float3 eyePosWorld; +uniform float4 rtParams0; +uniform float4 waterFogPlane; + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); +TORQUE_UNIFORM_SAMPLER2D(causticsTex0, 1); +TORQUE_UNIFORM_SAMPLER2D(causticsTex1, 2); + +float distanceToPlane(float4 plane, float3 pos) +{ + return (plane.x * pos.x + plane.y * pos.y + plane.z * pos.z) + plane.w; +} + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + //Sample the pre-pass + float4 prePass = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ); + + //Get depth + float depth = prePass.w; + if(depth > 0.9999) + return float4(0,0,0,0); + + //Get world position + float3 pos = eyePosWorld + IN.wsEyeRay * depth; + + // Check the water depth + float waterDepth = -distanceToPlane(waterFogPlane, pos); + if(waterDepth < 0) + return float4(0,0,0,0); + waterDepth = saturate(waterDepth); + + //Use world position X and Y to calculate caustics UV + float2 causticsUV0 = (abs(pos.xy * 0.25) % float2(1, 1)); + float2 causticsUV1 = (abs(pos.xy * 0.2) % float2(1, 1)); + + //Animate uvs + float timeSin = sin(accumTime); + causticsUV0.xy += float2(accumTime*0.1, timeSin*0.2); + causticsUV1.xy -= float2(accumTime*0.15, timeSin*0.15); + + //Sample caustics texture + float4 caustics = TORQUE_TEX2D(causticsTex0, causticsUV0); + caustics *= TORQUE_TEX2D(causticsTex1, causticsUV1); + + //Use normal Z to modulate caustics + //float waterDepth = 1 - saturate(pos.z + waterFogPlane.w + 1); + caustics *= saturate(prePass.z) * pow(abs(1-depth), 64) * waterDepth; + + return caustics; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/caustics/gl/causticsP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/caustics/gl/causticsP.glsl new file mode 100644 index 000000000..2d2a54154 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/caustics/gl/causticsP.glsl @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" +#include "shadergen:/autogenConditioners.h" + +uniform vec3 eyePosWorld; +uniform vec4 rtParams0; +uniform vec4 waterFogPlane; +uniform float accumTime; + +uniform sampler2D prepassTex; +uniform sampler2D causticsTex0; +uniform sampler2D causticsTex1; +uniform vec2 targetSize; + +out vec4 OUT_col; + +float distanceToPlane(vec4 plane, vec3 pos) +{ + return (plane.x * pos.x + plane.y * pos.y + plane.z * pos.z) + plane.w; +} + +void main() +{ + //Sample the pre-pass + vec4 prePass = prepassUncondition( prepassTex, IN_uv0 ); + + //Get depth + float depth = prePass.w; + if(depth > 0.9999) + { + OUT_col = vec4(0,0,0,0); + return; + } + + //Get world position + vec3 pos = eyePosWorld + IN_wsEyeRay * depth; + + // Check the water depth + float waterDepth = -distanceToPlane(waterFogPlane, pos); + if(waterDepth < 0) + { + OUT_col = vec4(0,0,0,0); + return; + } + waterDepth = saturate(waterDepth); + + //Use world position X and Y to calculate caustics UV + vec2 causticsUV0 = mod(abs(pos.xy * 0.25), vec2(1, 1)); + vec2 causticsUV1 = mod(abs(pos.xy * 0.2), vec2(1, 1)); + + //Animate uvs + float timeSin = sin(accumTime); + causticsUV0.xy += vec2(accumTime*0.1, timeSin*0.2); + causticsUV1.xy -= vec2(accumTime*0.15, timeSin*0.15); + + //Sample caustics texture + vec4 caustics = texture(causticsTex0, causticsUV0); + caustics *= texture(causticsTex1, causticsUV1); + + //Use normal Z to modulate caustics + //float waterDepth = 1 - saturate(pos.z + waterFogPlane.w + 1); + caustics *= saturate(prePass.z) * pow(1-depth, 64) * waterDepth; + + OUT_col = caustics; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/chromaticLens.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/chromaticLens.hlsl new file mode 100644 index 000000000..8fdca72b7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/chromaticLens.hlsl @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Based on 'Cubic Lens Distortion HLSL Shader' by François Tarlier +// www.francois-tarlier.com/blog/index.php/2009/11/cubic-lens-distortion-shader + +#include "./postFx.hlsl" +#include "./../torque.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); +uniform float distCoeff; +uniform float cubeDistort; +uniform float3 colorDistort; + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float2 tex = IN.uv0; + + float f = 0; + float r2 = (tex.x - 0.5) * (tex.x - 0.5) + (tex.y - 0.5) * (tex.y - 0.5); + + // Only compute the cubic distortion if necessary. + if ( cubeDistort == 0.0 ) + f = 1 + r2 * distCoeff; + else + f = 1 + r2 * (distCoeff + cubeDistort * sqrt(r2)); + + // Distort each color channel seperately to get a chromatic distortion effect. + float3 outColor; + float3 distort = f.xxx + colorDistort; + + for ( int i=0; i < 3; i++ ) + { + float x = distort[i] * ( tex.x - 0.5 ) + 0.5; + float y = distort[i] * ( tex.y - 0.5 ) + 0.5; + outColor[i] = TORQUE_TEX2DLOD( backBuffer, float4(x,y,0,0) )[i]; + } + + return float4( outColor.rgb, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl new file mode 100644 index 000000000..2f5835fc2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" + +// These are set by the game engine. +TORQUE_UNIFORM_SAMPLER2D(shrunkSampler, 0); // Output of DofDownsample() +TORQUE_UNIFORM_SAMPLER2D(blurredSampler, 1); // Blurred version of the shrunk sampler + + +// This is the pixel shader function that calculates the actual +// value used for the near circle of confusion. +// "texCoords" are 0 at the bottom left pixel and 1 at the top right. +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float3 color; + float coc; + half4 blurred; + half4 shrunk; + + shrunk = half4(TORQUE_TEX2D( shrunkSampler, IN.uv0 )); + blurred = half4(TORQUE_TEX2D( blurredSampler, IN.uv1 )); + color = shrunk.rgb; + //coc = shrunk.a; + //coc = blurred.a; + //coc = max( blurred.a, shrunk.a ); + coc = 2 * max( blurred.a, shrunk.a ) - shrunk.a; + + + //return float4( coc.rrr, 1.0 ); + //return float4( color, 1.0 ); + return float4( color, coc ); + //return float4( 1.0, 0.0, 1.0, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl new file mode 100644 index 000000000..8131e45cd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + +uniform float4 rtParams0; +uniform float4 rtParams1; +uniform float4 rtParams2; +uniform float4 rtParams3; + +PFXVertToPix main( PFXVert IN ) +{ + PFXVertToPix OUT; + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv; + OUT.uv1 = IN.uv; + OUT.uv2 = IN.uv; + OUT.uv3 = IN.uv; + */ + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv + rtParams0.xy; + OUT.uv1 = IN.uv + rtParams1.xy; + OUT.uv2 = IN.uv + rtParams2.xy; + OUT.uv3 = IN.uv + rtParams3.xy; + */ + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv * rtParams0.zw; + OUT.uv1 = IN.uv * rtParams1.zw; + OUT.uv2 = IN.uv * rtParams2.zw; + OUT.uv3 = IN.uv * rtParams3.zw; + */ + + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv0 = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( IN.uv, rtParams1 ); + OUT.uv2 = viewportCoordToRenderTarget( IN.uv, rtParams2 ); + OUT.uv3 = viewportCoordToRenderTarget( IN.uv, rtParams3 ); + + + OUT.wsEyeRay = IN.wsEyeRay; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_P.hlsl new file mode 100644 index 000000000..8c9028654 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_P.hlsl @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +// These are set by the game engine. +// The render target size is one-quarter the scene rendering size. +TORQUE_UNIFORM_SAMPLER2D(colorSampler, 0); +TORQUE_UNIFORM_SAMPLER2D(depthSampler, 1); +uniform float2 dofEqWorld; +uniform float2 targetSize; +uniform float depthOffset; +uniform float maxWorldCoC; +//uniform float2 dofEqWeapon; +//uniform float2 dofRowDelta; // float2( 0, 0.25 / renderTargetHeight ) + +struct Pixel +{ + float4 position : TORQUE_POSITION; + float2 tcColor0 : TEXCOORD0; + float2 tcColor1 : TEXCOORD1; + float2 tcDepth0 : TEXCOORD2; + float2 tcDepth1 : TEXCOORD3; + float2 tcDepth2 : TEXCOORD4; + float2 tcDepth3 : TEXCOORD5; +}; + +half4 main( Pixel IN ) : TORQUE_TARGET0 +{ + //return float4( 1.0, 0.0, 1.0, 1.0 ); + + float2 dofRowDelta = float2( 0, 0.25 / targetSize.y ); + + //float2 dofEqWorld = float2( -60, 1.0 ); + + half3 color; + half maxCoc; + float4 depth; + half4 viewCoc; + half4 sceneCoc; + half4 curCoc; + half4 coc; + float2 rowOfs[4]; + + // "rowOfs" reduces how many moves PS2.0 uses to emulate swizzling. + rowOfs[0] = 0; + rowOfs[1] = dofRowDelta.xy; + rowOfs[2] = dofRowDelta.xy * 2; + rowOfs[3] = dofRowDelta.xy * 3; + + // Use bilinear filtering to average 4 color samples for free. + color = 0; + color += half3(TORQUE_TEX2D( colorSampler, IN.tcColor0.xy + rowOfs[0] ).rgb); + color += half3(TORQUE_TEX2D(colorSampler, IN.tcColor1.xy + rowOfs[0]).rgb); + color += half3(TORQUE_TEX2D(colorSampler, IN.tcColor0.xy + rowOfs[2]).rgb); + color += half3(TORQUE_TEX2D(colorSampler, IN.tcColor1.xy + rowOfs[2]).rgb); + color /= 4; + + //declare thse here to save doing it in each loop below + half4 zero4 = half4(0, 0, 0, 0); + coc = zero4; + half4 dofEqWorld4X = half4(dofEqWorld.xxxx); + half4 dofEqWorld4Y = half4(dofEqWorld.yyyy); + half4 maxWorldCoC4 = half4(maxWorldCoC, maxWorldCoC, maxWorldCoC, maxWorldCoC); + // Process 4 samples at a time to use vector hardware efficiently. + // The CoC will be 1 if the depth is negative, so use "min" to pick + // between "sceneCoc" and "viewCoc". + [unroll] // coc[i] causes this anyway + for (int i = 0; i < 4; i++) + { + depth[0] = TORQUE_PREPASS_UNCONDITION(depthSampler, (IN.tcDepth0.xy + rowOfs[i])).w; + depth[1] = TORQUE_PREPASS_UNCONDITION(depthSampler, (IN.tcDepth1.xy + rowOfs[i])).w; + depth[2] = TORQUE_PREPASS_UNCONDITION(depthSampler, (IN.tcDepth2.xy + rowOfs[i])).w; + depth[3] = TORQUE_PREPASS_UNCONDITION(depthSampler, (IN.tcDepth3.xy + rowOfs[i])).w; + + coc = max(coc, clamp(dofEqWorld4X * half4(depth)+dofEqWorld4Y, zero4, maxWorldCoC4)); + } + + /* + depth[0] = TORQUE_TEX2D( depthSampler, pixel.tcDepth0.xy + rowOfs[0] ).r; + depth[1] = TORQUE_TEX2D( depthSampler, pixel.tcDepth1.xy + rowOfs[0] ).r; + depth[2] = TORQUE_TEX2D( depthSampler, pixel.tcDepth2.xy + rowOfs[0] ).r; + depth[3] = TORQUE_TEX2D( depthSampler, pixel.tcDepth3.xy + rowOfs[0] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = curCoc; + + depth[0] = TORQUE_TEX2D( depthSampler, pixel.tcDepth0.xy + rowOfs[1] ).r; + depth[1] = TORQUE_TEX2D( depthSampler, pixel.tcDepth1.xy + rowOfs[1] ).r; + depth[2] = TORQUE_TEX2D( depthSampler, pixel.tcDepth2.xy + rowOfs[1] ).r; + depth[3] = TORQUE_TEX2D( depthSampler, pixel.tcDepth3.xy + rowOfs[1] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + + depth[0] = TORQUE_TEX2D( depthSampler, pixel.tcDepth0.xy + rowOfs[2] ).r; + depth[1] = TORQUE_TEX2D( depthSampler, pixel.tcDepth1.xy + rowOfs[2] ).r; + depth[2] = TORQUE_TEX2D( depthSampler, pixel.tcDepth2.xy + rowOfs[2] ).r; + depth[3] = TORQUE_TEX2D( depthSampler, pixel.tcDepth3.xy + rowOfs[2] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + + depth[0] = TORQUE_TEX2D( depthSampler, pixel.tcDepth0.xy + rowOfs[3] ).r; + depth[1] = TORQUE_TEX2D( depthSampler, pixel.tcDepth1.xy + rowOfs[3] ).r; + depth[2] = TORQUE_TEX2D( depthSampler, pixel.tcDepth2.xy + rowOfs[3] ).r; + depth[3] = TORQUE_TEX2D( depthSampler, pixel.tcDepth3.xy + rowOfs[3] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + */ + + maxCoc = max( max( coc[0], coc[1] ), max( coc[2], coc[3] ) ); + + //return half4( 1.0, 0.0, 1.0, 1.0 ); + return half4( color, maxCoc ); + //return half4( color, 1.0f ); + //return half4( maxCoc.rrr, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_V.hlsl new file mode 100644 index 000000000..0b3ec01e2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_DownSample_V.hlsl @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + +struct Vert +{ + float3 pos : POSITION; + float2 tc : TEXCOORD0; + float3 wsEyeRay : TEXCOORD1; +}; + +struct Pixel +{ + float4 position : TORQUE_POSITION; + float2 tcColor0 : TEXCOORD0; + float2 tcColor1 : TEXCOORD1; + float2 tcDepth0 : TEXCOORD2; + float2 tcDepth1 : TEXCOORD3; + float2 tcDepth2 : TEXCOORD4; + float2 tcDepth3 : TEXCOORD5; +}; + +uniform float4 rtParams0; +uniform float2 oneOverTargetSize; + +Pixel main( Vert IN ) +{ + Pixel OUT; + OUT.position = float4(IN.pos,1.0); + + float2 uv = viewportCoordToRenderTarget( IN.tc, rtParams0 ); + //OUT.position = mul( IN.pos, modelView ); + OUT.tcColor1 = uv + float2( +1.0, -0.0 ) * oneOverTargetSize; + OUT.tcColor0 = uv + float2( -1.0, -0.0 ) * oneOverTargetSize; + OUT.tcDepth0 = uv + float2( -0.5, -0.0 ) * oneOverTargetSize; + OUT.tcDepth1 = uv + float2( -1.5, -0.0 ) * oneOverTargetSize; + OUT.tcDepth2 = uv + float2( +1.5, -0.0 ) * oneOverTargetSize; + OUT.tcDepth3 = uv + float2( +2.5, -0.0 ) * oneOverTargetSize; + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_P.hlsl new file mode 100644 index 000000000..cb7342d40 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_P.hlsl @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModelAutoGen.hlsl" +#include "./../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(colorSampler,0); // Original source image +TORQUE_UNIFORM_SAMPLER2D(smallBlurSampler,1); // Output of SmallBlurPS() +TORQUE_UNIFORM_SAMPLER2D(largeBlurSampler,2); // Blurred output of DofDownsample() +TORQUE_UNIFORM_SAMPLER2D(depthSampler,3); + +uniform float2 oneOverTargetSize; +uniform float4 dofLerpScale; +uniform float4 dofLerpBias; +uniform float3 dofEqFar; +uniform float maxFarCoC; + +//static float d0 = 0.1; +//static float d1 = 0.1; +//static float d2 = 0.8; +//static float4 dofLerpScale = float4( -1.0 / d0, -1.0 / d1, -1.0 / d2, 1.0 / d2 ); +//static float4 dofLerpBias = float4( 1.0, (1.0 - d2) / d1, 1.0 / d2, (d2 - 1.0) / d2 ); +//static float3 dofEqFar = float3( 2.0, 0.0, 1.0 ); + +float4 tex2Doffset(TORQUE_SAMPLER2D(s), float2 tc, float2 offset) +{ + return TORQUE_TEX2D( s, tc + offset * oneOverTargetSize ); +} + +half3 GetSmallBlurSample( float2 tc ) +{ + half3 sum; + const half weight = 4.0 / 17; + sum = 0; // Unblurred sample done by alpha blending + //sum += weight * tex2Doffset( colorSampler, tc, float2( 0, 0 ) ).rgb; + sum += weight * half3(tex2Doffset(TORQUE_SAMPLER2D_MAKEARG(colorSampler), tc, float2(+0.5, -1.5)).rgb); + sum += weight * half3(tex2Doffset(TORQUE_SAMPLER2D_MAKEARG(colorSampler), tc, float2(-1.5, -0.5)).rgb); + sum += weight * half3(tex2Doffset(TORQUE_SAMPLER2D_MAKEARG(colorSampler), tc, float2(-0.5, +1.5)).rgb); + sum += weight * half3(tex2Doffset(TORQUE_SAMPLER2D_MAKEARG(colorSampler), tc, float2(+1.5, +0.5)).rgb); + return sum; +} + +half4 InterpolateDof( half3 small, half3 med, half3 large, half t ) +{ + //t = 2; + half4 weights; + half3 color; + half alpha; + + // Efficiently calculate the cross-blend weights for each sample. + // Let the unblurred sample to small blur fade happen over distance + // d0, the small to medium blur over distance d1, and the medium to + // large blur over distance d2, where d0 + d1 + d2 = 1. + //float4 dofLerpScale = float4( -1 / d0, -1 / d1, -1 / d2, 1 / d2 ); + //float4 dofLerpBias = float4( 1, (1 – d2) / d1, 1 / d2, (d2 – 1) / d2 ); + + weights = half4(saturate( t * dofLerpScale + dofLerpBias )); + weights.yz = min( weights.yz, 1 - weights.xy ); + + // Unblurred sample with weight "weights.x" done by alpha blending + color = weights.y * small + weights.z * med + weights.w * large; + //color = med; + alpha = dot( weights.yzw, half3( 16.0 / 17, 1.0, 1.0 ) ); + //alpha = 0.0; + + return half4( color, alpha ); +} + +half4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + //return half4( 1,0,1,1 ); + //return half4( TORQUE_TEX2D( colorSampler, IN.uv0 ).rgb, 1.0 ); + //return half4( TORQUE_TEX2D( colorSampler, texCoords ).rgb, 0 ); + half3 small; + half4 med; + half3 large; + half depth; + half nearCoc; + half farCoc; + half coc; + + small = GetSmallBlurSample( IN.uv0 ); + //small = half3( 1,0,0 ); + //return half4( small, 1.0 ); + med = half4(TORQUE_TEX2D( smallBlurSampler, IN.uv1 )); + //med.rgb = half3( 0,1,0 ); + //return half4(med.rgb, 0.0); + large = half3(TORQUE_TEX2D(largeBlurSampler, IN.uv2).rgb); + //large = half3( 0,0,1 ); + //return large; + //return half4(large.rgb,1.0); + nearCoc = med.a; + + // Since the med blur texture is screwed up currently + // replace it with the large, but this needs to be fixed. + //med.rgb = large; + + //nearCoc = 0; + depth = half(TORQUE_PREPASS_UNCONDITION( depthSampler, IN.uv3 ).w); + //return half4(depth.rrr,1); + //return half4(nearCoc.rrr,1.0); + + if (depth > 0.999 ) + { + coc = nearCoc; // We don't want to blur the sky. + //coc = 0; + } + else + { + // dofEqFar.x and dofEqFar.y specify the linear ramp to convert + // to depth for the distant out-of-focus region. + // dofEqFar.z is the ratio of the far to the near blur radius. + farCoc = half(clamp( dofEqFar.x * depth + dofEqFar.y, 0.0, maxFarCoC )); + coc = half(max( nearCoc, farCoc * dofEqFar.z )); + //coc = nearCoc; + } + + //coc = nearCoc; + //coc = farCoc; + //return half4(coc.rrr,0.5); + //return half4(farCoc.rrr,1); + //return half4(nearCoc.rrr,1); + + //return half4( 1,0,1,0 ); + return InterpolateDof( small, med.rgb, large, coc ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_V.hlsl new file mode 100644 index 000000000..86c93701a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Final_V.hlsl @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + +uniform float4 rtParams0; +uniform float4 rtParams1; +uniform float4 rtParams2; +uniform float4 rtParams3; +uniform float2 oneOverTargetSize; + +PFXVertToPix main( PFXVert IN ) +{ + PFXVertToPix OUT; + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv; + OUT.uv1 = IN.uv; + OUT.uv2 = IN.uv; + OUT.uv3 = IN.uv; + */ + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv + rtParams0.xy; + OUT.uv1 = IN.uv + rtParams1.xy; + OUT.uv2 = IN.uv + rtParams2.xy; + OUT.uv3 = IN.uv + rtParams3.xy; + */ + + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv * rtParams0.zw; + OUT.uv1 = IN.uv * rtParams1.zw; + OUT.uv2 = IN.uv * rtParams2.zw; + OUT.uv3 = IN.uv * rtParams3.zw; + */ + + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv0 = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( IN.uv, rtParams1 ); // + float2( -5, 1 ) * oneOverTargetSize; + OUT.uv2 = viewportCoordToRenderTarget( IN.uv, rtParams2 ); + OUT.uv3 = viewportCoordToRenderTarget( IN.uv, rtParams3 ); + + + OUT.wsEyeRay = IN.wsEyeRay; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_P.hlsl new file mode 100644 index 000000000..f4d29f3e1 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_P.hlsl @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ + float4 kernel = float4( 0.175, 0.275, 0.375, 0.475 ) * 0.5 / 1.3; //25f; + + float4 OUT = 0; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv0 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv1 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv2 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv3 ) * kernel.w; + + OUT += TORQUE_TEX2D( diffuseMap, IN.uv4 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv5 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv6 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + //float3 rgb2lum = float3( 0.30, 0.59, 0.11 ); + //OUT.a = dot( OUT.rgb, rgb2lum ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_V.hlsl new file mode 100644 index 000000000..b2d4582e0 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Gausian_V.hlsl @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + + +uniform float4 rtParams0; +uniform float2 texSize0; +uniform float2 oneOverTargetSize; + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +VertToPix main( PFXVert IN ) +{ + VertToPix OUT; + + OUT.hpos = float4(IN.pos,1.0); + + IN.uv = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + + // I don't know why this offset is necessary, but it is. + //IN.uv = IN.uv * oneOverTargetSize; + + OUT.uv0 = IN.uv + ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT.uv1 = IN.uv + ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT.uv2 = IN.uv + ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT.uv3 = IN.uv + ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + OUT.uv4 = IN.uv - ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT.uv5 = IN.uv - ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT.uv6 = IN.uv - ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT.uv7 = IN.uv - ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + /* + OUT.uv0 = viewportCoordToRenderTarget( OUT.uv0, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( OUT.uv1, rtParams0 ); + OUT.uv2 = viewportCoordToRenderTarget( OUT.uv2, rtParams0 ); + OUT.uv3 = viewportCoordToRenderTarget( OUT.uv3, rtParams0 ); + + OUT.uv4 = viewportCoordToRenderTarget( OUT.uv4, rtParams0 ); + OUT.uv5 = viewportCoordToRenderTarget( OUT.uv5, rtParams0 ); + OUT.uv6 = viewportCoordToRenderTarget( OUT.uv6, rtParams0 ); + OUT.uv7 = viewportCoordToRenderTarget( OUT.uv7, rtParams0 ); + */ + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Passthrough_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Passthrough_V.hlsl new file mode 100644 index 000000000..8131e45cd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_Passthrough_V.hlsl @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + +uniform float4 rtParams0; +uniform float4 rtParams1; +uniform float4 rtParams2; +uniform float4 rtParams3; + +PFXVertToPix main( PFXVert IN ) +{ + PFXVertToPix OUT; + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv; + OUT.uv1 = IN.uv; + OUT.uv2 = IN.uv; + OUT.uv3 = IN.uv; + */ + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv + rtParams0.xy; + OUT.uv1 = IN.uv + rtParams1.xy; + OUT.uv2 = IN.uv + rtParams2.xy; + OUT.uv3 = IN.uv + rtParams3.xy; + */ + + /* + OUT.hpos = IN.pos; + OUT.uv0 = IN.uv * rtParams0.zw; + OUT.uv1 = IN.uv * rtParams1.zw; + OUT.uv2 = IN.uv * rtParams2.zw; + OUT.uv3 = IN.uv * rtParams3.zw; + */ + + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv0 = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( IN.uv, rtParams1 ); + OUT.uv2 = viewportCoordToRenderTarget( IN.uv, rtParams2 ); + OUT.uv3 = viewportCoordToRenderTarget( IN.uv, rtParams3 ); + + + OUT.wsEyeRay = IN.wsEyeRay; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl new file mode 100644 index 000000000..175525a91 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// This vertex and pixel shader applies a 3 x 3 blur to the image in +// colorMapSampler, which is the same size as the render target. +// The sample weights are 1/16 in the corners, 2/16 on the edges, +// and 4/16 in the center. +#include "../../shaderModel.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(colorSampler, 0); // Output of DofNearCoc() + +struct Pixel +{ + float4 position : TORQUE_POSITION; + float4 texCoords : TEXCOORD0; +}; + +float4 main( Pixel IN ) : TORQUE_TARGET0 +{ + float4 color; + color = 0.0; + color += TORQUE_TEX2D( colorSampler, IN.texCoords.xz ); + color += TORQUE_TEX2D( colorSampler, IN.texCoords.yz ); + color += TORQUE_TEX2D( colorSampler, IN.texCoords.xw ); + color += TORQUE_TEX2D( colorSampler, IN.texCoords.yw ); + return color / 4.0; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl new file mode 100644 index 000000000..3edb1ec2a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// This vertex and pixel shader applies a 3 x 3 blur to the image in +// colorMapSampler, which is the same size as the render target. +// The sample weights are 1/16 in the corners, 2/16 on the edges, +// and 4/16 in the center. + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + +struct Vert +{ + float3 position : POSITION; + float2 texCoords : TEXCOORD0; +}; + +struct Pixel +{ + float4 position : TORQUE_POSITION; + float4 texCoords : TEXCOORD0; +}; + +uniform float2 oneOverTargetSize; +uniform float4 rtParams0; + +Pixel main( Vert IN ) +{ + Pixel OUT; + const float4 halfPixel = { -0.5, 0.5, -0.5, 0.5 }; + OUT.position = float4(IN.position,1.0); //Transform_ObjectToClip( IN.position ); + + //float2 uv = IN.texCoords + rtParams0.xy; + float2 uv = viewportCoordToRenderTarget( IN.texCoords, rtParams0 ); + OUT.texCoords = uv.xxyy + halfPixel * oneOverTargetSize.xxyy; + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl new file mode 100644 index 000000000..38cb099c4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" + +// These are set by the game engine. +uniform sampler2D shrunkSampler; // Output of DofDownsample() +uniform sampler2D blurredSampler; // Blurred version of the shrunk sampler + +out vec4 OUT_col; + +// This is the pixel shader function that calculates the actual +// value used for the near circle of confusion. +// "texCoords" are 0 at the bottom left pixel and 1 at the top right. +void main() +{ + vec3 color; + float coc; + half4 blurred; + half4 shrunk; + + shrunk = texture( shrunkSampler, IN_uv0 ); + blurred = texture( blurredSampler, IN_uv1 ); + color = shrunk.rgb; + //coc = shrunk.a; + //coc = blurred.a; + //coc = max( blurred.a, shrunk.a ); + coc = 2 * max( blurred.a, shrunk.a ) - shrunk.a; + + + //OUT_col = vec4( coc.rrr, 1.0 ); + //OUT_col = vec4( color, 1.0 ); + OUT_col = vec4( color, coc ); + //OUT_col = vec4( 1.0, 0.0, 1.0, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl new file mode 100644 index 000000000..d02ce6551 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" +#include "../../gl/postFX.glsl" + +uniform vec4 rtParams0; +uniform vec4 rtParams1; +uniform vec4 rtParams2; +uniform vec4 rtParams3; + +void main() +{ + /* + OUT_hpos = IN.pos; + OUT_uv0 = IN_uv; + OUT_uv1 = IN_uv; + OUT_uv2 = IN_uv; + OUT_uv3 = IN_uv; + */ + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv + rtParams0.xy; + OUT_uv1 = IN_uv + rtParams1.xy; + OUT_uv2 = IN_uv + rtParams2.xy; + OUT_uv3 = IN_uv + rtParams3.xy; + */ + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv * rtParams0.zw; + OUT_uv1 = IN_uv * rtParams1.zw; + OUT_uv2 = IN_uv * rtParams2.zw; + OUT_uv3 = IN_uv * rtParams3.zw; + */ + + + OUT_hpos = IN_pos; + OUT_uv0 = viewportCoordToRenderTarget( IN_uv, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( IN_uv, rtParams1 ); + OUT_uv2 = viewportCoordToRenderTarget( IN_uv, rtParams2 ); + OUT_uv3 = viewportCoordToRenderTarget( IN_uv, rtParams3 ); + + + OUT_wsEyeRay = IN_wsEyeRay; + + correctSSP(gl_Position);; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl new file mode 100644 index 000000000..17f23e487 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +// These are set by the game engine. +// The render target size is one-quarter the scene rendering size. +uniform sampler2D colorSampler; +uniform sampler2D depthSampler; +uniform vec2 dofEqWorld; +uniform float depthOffset; +uniform vec2 targetSize; +uniform float maxWorldCoC; +//uniform vec2 dofEqWeapon; +//uniform vec2 dofRowDelta; // vec2( 0, 0.25 / renderTargetHeight ) + +in vec2 tcColor0; +#define IN_tcColor0 tcColor0 +in vec2 tcColor1; +#define IN_tcColor1 tcColor1 +in vec2 tcDepth0; +#define IN_tcDepth0 tcDepth0 +in vec2 tcDepth1; +#define IN_tcDepth1 tcDepth1 +in vec2 tcDepth2; +#define IN_tcDepth2 tcDepth2 +in vec2 tcDepth3; +#define IN_tcDepth3 tcDepth3 + +out vec4 OUT_col; + +void main() +{ + //return vec4( 1.0, 0.0, 1.0, 1.0 ); + + vec2 dofRowDelta = vec2( 0, 0.25 / targetSize.y ); + + //vec2 dofEqWorld = vec2( -60, 1.0 ); + + half3 color; + half maxCoc; + vec4 depth; + half4 viewCoc; + half4 sceneCoc; + half4 curCoc; + half4 coc; + vec2 rowOfs[4]; + + // "rowOfs" reduces how many moves PS2.0 uses to emulate swizzling. + rowOfs[0] = vec2(0); + rowOfs[1] = dofRowDelta.xy; + rowOfs[2] = dofRowDelta.xy * 2; + rowOfs[3] = dofRowDelta.xy * 3; + + // Use bilinear filtering to average 4 color samples for free. + color = half3(0); + color += texture( colorSampler, IN_tcColor0.xy + rowOfs[0] ).rgb; + color += texture( colorSampler, IN_tcColor1.xy + rowOfs[0] ).rgb; + color += texture( colorSampler, IN_tcColor0.xy + rowOfs[2] ).rgb; + color += texture( colorSampler, IN_tcColor1.xy + rowOfs[2] ).rgb; + color /= 4; + + // Process 4 samples at a time to use vector hardware efficiently. + // The CoC will be 1 if the depth is negative, so use "min" to pick + // between "sceneCoc" and "viewCoc". + + coc = half4(0); + for ( int i = 0; i < 4; i++ ) + { + depth[0] = prepassUncondition( depthSampler, ( IN_tcDepth0.xy + rowOfs[i] ) ).w; + depth[1] = prepassUncondition( depthSampler, ( IN_tcDepth1.xy + rowOfs[i] ) ).w; + depth[2] = prepassUncondition( depthSampler, ( IN_tcDepth2.xy + rowOfs[i] ) ).w; + depth[3] = prepassUncondition( depthSampler, ( IN_tcDepth3.xy + rowOfs[i] ) ).w; + + // @todo OPENGL INTEL need review + coc = max( coc, clamp( half4(dofEqWorld.x) * depth + half4(dofEqWorld.y), half4(0.0), half4(maxWorldCoC) ) ); + } + + /* + depth[0] = texture( depthSampler, pixel.tcDepth0.xy + rowOfs[0] ).r; + depth[1] = texture( depthSampler, pixel.tcDepth1.xy + rowOfs[0] ).r; + depth[2] = texture( depthSampler, pixel.tcDepth2.xy + rowOfs[0] ).r; + depth[3] = texture( depthSampler, pixel.tcDepth3.xy + rowOfs[0] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = curCoc; + + depth[0] = texture( depthSampler, pixel.tcDepth0.xy + rowOfs[1] ).r; + depth[1] = texture( depthSampler, pixel.tcDepth1.xy + rowOfs[1] ).r; + depth[2] = texture( depthSampler, pixel.tcDepth2.xy + rowOfs[1] ).r; + depth[3] = texture( depthSampler, pixel.tcDepth3.xy + rowOfs[1] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + + depth[0] = texture( depthSampler, pixel.tcDepth0.xy + rowOfs[2] ).r; + depth[1] = texture( depthSampler, pixel.tcDepth1.xy + rowOfs[2] ).r; + depth[2] = texture( depthSampler, pixel.tcDepth2.xy + rowOfs[2] ).r; + depth[3] = texture( depthSampler, pixel.tcDepth3.xy + rowOfs[2] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + + depth[0] = texture( depthSampler, pixel.tcDepth0.xy + rowOfs[3] ).r; + depth[1] = texture( depthSampler, pixel.tcDepth1.xy + rowOfs[3] ).r; + depth[2] = texture( depthSampler, pixel.tcDepth2.xy + rowOfs[3] ).r; + depth[3] = texture( depthSampler, pixel.tcDepth3.xy + rowOfs[3] ).r; + viewCoc = saturate( dofEqWeapon.x * -depth + dofEqWeapon.y ); + sceneCoc = saturate( dofEqWorld.x * depth + dofEqWorld.y ); + curCoc = min( viewCoc, sceneCoc ); + coc = max( coc, curCoc ); + */ + + maxCoc = max( max( coc[0], coc[1] ), max( coc[2], coc[3] ) ); + + //OUT_col = half4( 1.0, 0.0, 1.0, 1.0 ); + OUT_col = half4( color, maxCoc ); + //OUT_col = half4( color, 1.0f ); + //OUT_col = half4( maxCoc.rrr, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl new file mode 100644 index 000000000..b8e840c9e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; +in vec3 vTexCoord1; + +#define IN_pos vPosition +#define IN_tc vTexCoord0 +#define IN_wsEyeRay vTexCoord1 + +#define OUT_position gl_Position + +out vec2 tcColor0; +#define OUT_tcColor0 tcColor0 +out vec2 tcColor1; +#define OUT_tcColor1 tcColor1 +out vec2 tcDepth0; +#define OUT_tcDepth0 tcDepth0 +out vec2 tcDepth1; +#define OUT_tcDepth1 tcDepth1 +out vec2 tcDepth2; +#define OUT_tcDepth2 tcDepth2 +out vec2 tcDepth3; +#define OUT_tcDepth3 tcDepth3 + + +uniform vec4 rtParams0; +uniform vec2 oneOverTargetSize; + +void main() +{ + OUT_position = IN_pos; + + vec2 uv = viewportCoordToRenderTarget( IN_tc, rtParams0 ); + //OUT_position = tMul( IN_pos, modelView ); + OUT_tcColor1 = uv + vec2( +1.0, -0.0 ) * oneOverTargetSize; + OUT_tcColor0 = uv + vec2( -1.0, -0.0 ) * oneOverTargetSize; + OUT_tcDepth0 = uv + vec2( -0.5, -0.0 ) * oneOverTargetSize; + OUT_tcDepth1 = uv + vec2( -1.5, -0.0 ) * oneOverTargetSize; + OUT_tcDepth2 = uv + vec2( +1.5, -0.0 ) * oneOverTargetSize; + OUT_tcDepth3 = uv + vec2( +2.5, -0.0 ) * oneOverTargetSize; + + correctSSP(gl_Position); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_P.glsl new file mode 100644 index 000000000..40b71bc27 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_P.glsl @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D colorSampler; // Original source image +uniform sampler2D smallBlurSampler; // Output of SmallBlurPS() +uniform sampler2D largeBlurSampler; // Blurred output of DofDownsample() +uniform sampler2D depthSampler; // +uniform vec2 oneOverTargetSize; +uniform vec4 dofLerpScale; +uniform vec4 dofLerpBias; +uniform vec3 dofEqFar; +uniform float maxFarCoC; + +//static float d0 = 0.1; +//static float d1 = 0.1; +//static float d2 = 0.8; +//static vec4 dofLerpScale = vec4( -1.0 / d0, -1.0 / d1, -1.0 / d2, 1.0 / d2 ); +//static vec4 dofLerpBias = vec4( 1.0, (1.0 - d2) / d1, 1.0 / d2, (d2 - 1.0) / d2 ); +//static vec3 dofEqFar = vec3( 2.0, 0.0, 1.0 ); + +out vec4 OUT_col; + +vec4 tex2Doffset( sampler2D s, vec2 tc, vec2 offset ) +{ + return texture( s, tc + offset * oneOverTargetSize ); +} + +half3 GetSmallBlurSample( vec2 tc ) +{ + half3 sum; + const half weight = 4.0 / 17; + sum = half3(0); // Unblurred sample done by alpha blending + //sum += weight * tex2Doffset( colorSampler, tc, vec2( 0, 0 ) ).rgb; + sum += weight * tex2Doffset( colorSampler, tc, vec2( +0.5, -1.5 ) ).rgb; + sum += weight * tex2Doffset( colorSampler, tc, vec2( -1.5, -0.5 ) ).rgb; + sum += weight * tex2Doffset( colorSampler, tc, vec2( -0.5, +1.5 ) ).rgb; + sum += weight * tex2Doffset( colorSampler, tc, vec2( +1.5, +0.5 ) ).rgb; + return sum; +} + +half4 InterpolateDof( half3 small, half3 med, half3 large, half t ) +{ + //t = 2; + half4 weights; + half3 color; + half alpha; + + // Efficiently calculate the cross-blend weights for each sample. + // Let the unblurred sample to small blur fade happen over distance + // d0, the small to medium blur over distance d1, and the medium to + // large blur over distance d2, where d0 + d1 + d2 = 1. + //vec4 dofLerpScale = vec4( -1 / d0, -1 / d1, -1 / d2, 1 / d2 ); + //vec4 dofLerpBias = vec4( 1, (1 – d2) / d1, 1 / d2, (d2 – 1) / d2 ); + + weights = saturate( t * dofLerpScale + dofLerpBias ); + weights.yz = min( weights.yz, 1 - weights.xy ); + + // Unblurred sample with weight "weights.x" done by alpha blending + color = weights.y * small + weights.z * med + weights.w * large; + //color = med; + alpha = dot( weights.yzw, half3( 16.0 / 17, 1.0, 1.0 ) ); + //alpha = 0.0; + + return half4( color, alpha ); +} + +void main() +{ + //return half4( 1,0,1,1 ); + //return half4( texture( colorSampler, IN_uv0 ).rgb, 1.0 ); + //return half4( texture( colorSampler, texCoords ).rgb, 0 ); + half3 small; + half4 med; + half3 large; + half depth; + half nearCoc; + half farCoc; + half coc; + + small = GetSmallBlurSample( IN_uv0 ); + //small = half3( 1,0,0 ); + //return half4( small, 1.0 ); + med = texture( smallBlurSampler, IN_uv1 ); + //med.rgb = half3( 0,1,0 ); + //return half4(med.rgb, 0.0); + large = texture( largeBlurSampler, IN_uv2 ).rgb; + //large = half3( 0,0,1 ); + //return large; + //return half4(large.rgb,1.0); + nearCoc = med.a; + + // Since the med blur texture is screwed up currently + // replace it with the large, but this needs to be fixed. + //med.rgb = large; + + //nearCoc = 0; + depth = prepassUncondition( depthSampler, IN_uv3 ).w; + //return half4(depth.rrr,1); + //return half4(nearCoc.rrr,1.0); + + if (depth > 0.999 ) + { + coc = nearCoc; // We don't want to blur the sky. + //coc = 0; + } + else + { + // dofEqFar.x and dofEqFar.y specify the linear ramp to convert + // to depth for the distant out-of-focus region. + // dofEqFar.z is the ratio of the far to the near blur radius. + farCoc = clamp( dofEqFar.x * depth + dofEqFar.y, 0.0, maxFarCoC ); + coc = max( nearCoc, farCoc * dofEqFar.z ); + //coc = nearCoc; + } + + //coc = nearCoc; + //coc = farCoc; + //return half4(coc.rrr,0.5); + //return half4(farCoc.rrr,1); + //return half4(nearCoc.rrr,1); + + //return half4( 1,0,1,0 ); + OUT_col = InterpolateDof( small, med.rgb, large, coc ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_V.glsl new file mode 100644 index 000000000..abc91246e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Final_V.glsl @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" +#include "../../gl/postFX.glsl" + +uniform vec4 rtParams0; +uniform vec4 rtParams1; +uniform vec4 rtParams2; +uniform vec4 rtParams3; +uniform vec2 oneOverTargetSize; + +void main() +{ + /* + OUT.hpos = IN_pos; + OUT_uv0 = IN_uv; + OUT_uv1 = IN_uv; + OUT_uv2 = IN_uv; + OUT_uv3 = IN_uv; + */ + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv + rtParams0.xy; + OUT_uv1 = IN_uv + rtParams1.xy; + OUT_uv2 = IN_uv + rtParams2.xy; + OUT_uv3 = IN_uv + rtParams3.xy; + */ + + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv * rtParams0.zw; + OUT_uv1 = IN_uv * rtParams1.zw; + OUT_uv2 = IN_uv * rtParams2.zw; + OUT_uv3 = IN_uv * rtParams3.zw; + */ + + + OUT_hpos = IN_pos; + OUT_uv0 = viewportCoordToRenderTarget( IN_uv, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( IN_uv, rtParams1 ); // + vec2( -5, 1 ) * oneOverTargetSize; + OUT_uv2 = viewportCoordToRenderTarget( IN_uv, rtParams2 ); + OUT_uv3 = viewportCoordToRenderTarget( IN_uv, rtParams3 ); + + + OUT_wsEyeRay = IN_wsEyeRay; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl new file mode 100644 index 000000000..61e7697af --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec3 wsEyeRay; +#define IN_wsEyeRay wsEyeRay +in vec2 uv0; +#define IN_uv0 uv0 +in vec2 uv1; +#define IN_uv1 uv1 +in vec2 uv2; +#define IN_uv2 uv2 +in vec2 uv3; +#define IN_uv3 uv3 +in vec2 uv4; +#define IN_uv4 uv4 +in vec2 uv5; +#define IN_uv5 uv5 +in vec2 uv6; +#define IN_uv6 uv6 +in vec2 uv7; +#define IN_uv7 uv7 + +out vec4 OUT_col; + +uniform sampler2D diffuseMap; + +void main() +{ + vec4 kernel = vec4( 0.175, 0.275, 0.375, 0.475 ) * 0.5 / 1.3; //25f; + + OUT_col = vec4(0); + OUT_col += texture( diffuseMap, IN_uv0 ) * kernel.x; + OUT_col += texture( diffuseMap, IN_uv1 ) * kernel.y; + OUT_col += texture( diffuseMap, IN_uv2 ) * kernel.z; + OUT_col += texture( diffuseMap, IN_uv3 ) * kernel.w; + + OUT_col += texture( diffuseMap, IN_uv4 ) * kernel.x; + OUT_col += texture( diffuseMap, IN_uv5 ) * kernel.y; + OUT_col += texture( diffuseMap, IN_uv6 ) * kernel.z; + OUT_col += texture( diffuseMap, IN_uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + //vec3 rgb2lum = vec3( 0.30, 0.59, 0.11 ); + //OUT_col.a = dot( OUT_col.rgb, rgb2lum ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl new file mode 100644 index 000000000..c77e23c53 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; +in vec3 vTexCoord1; + +#define IN_pos vPosition +#define _IN_uv vTexCoord0 +#define IN_wsEyeRay vTexCoord1 + +#define OUT_hpos gl_Position +out vec3 wsEyeRay; +#define OUT_wsEyeRay wsEyeRay +out vec2 uv0; +#define OUT_uv0 uv0 +out vec2 uv1; +#define OUT_uv1 uv1 +out vec2 uv2; +#define OUT_uv2 uv2 +out vec2 uv3; +#define OUT_uv3 uv3 +out vec2 uv4; +#define OUT_uv4 uv4 +out vec2 uv5; +#define OUT_uv5 uv5 +out vec2 uv6; +#define OUT_uv6 uv6 +out vec2 uv7; +#define OUT_uv7 uv7 + +uniform vec2 texSize0; +uniform vec4 rtParams0; +uniform vec2 oneOverTargetSize; + + +void main() +{ + OUT_hpos = IN_pos; + + vec2 IN_uv = viewportCoordToRenderTarget( _IN_uv, rtParams0 ); + + // I don't know why this offset is necessary, but it is. + //IN_uv = IN_uv * oneOverTargetSize; + + OUT_uv0 = IN_uv + ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT_uv1 = IN_uv + ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT_uv2 = IN_uv + ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT_uv3 = IN_uv + ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + OUT_uv4 = IN_uv - ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT_uv5 = IN_uv - ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT_uv6 = IN_uv - ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT_uv7 = IN_uv - ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + /* + OUT_uv0 = viewportCoordToRenderTarget( OUT_uv0, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( OUT_uv1, rtParams0 ); + OUT_uv2 = viewportCoordToRenderTarget( OUT_uv2, rtParams0 ); + OUT_uv3 = viewportCoordToRenderTarget( OUT_uv3, rtParams0 ); + + OUT_uv4 = viewportCoordToRenderTarget( OUT_uv4, rtParams0 ); + OUT_uv5 = viewportCoordToRenderTarget( OUT_uv5, rtParams0 ); + OUT_uv6 = viewportCoordToRenderTarget( OUT_uv6, rtParams0 ); + OUT_uv7 = viewportCoordToRenderTarget( OUT_uv7, rtParams0 ); + */ + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Passthrough_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Passthrough_V.glsl new file mode 100644 index 000000000..bd02fb7d4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_Passthrough_V.glsl @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" +#include "../../gl/postFX.glsl" + +uniform vec4 rtParams0; +uniform vec4 rtParams1; +uniform vec4 rtParams2; +uniform vec4 rtParams3; + +void main() +{ + /* + OUT.hpos = IN_pos; + OUT_uv0 = IN_uv; + OUT_uv1 = IN_uv; + OUT_uv2 = IN_uv; + OUT_uv3 = IN_uv; + */ + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv + rtParams0.xy; + OUT_uv1 = IN_uv + rtParams1.xy; + OUT_uv2 = IN_uv + rtParams2.xy; + OUT_uv3 = IN_uv + rtParams3.xy; + */ + + /* + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv * rtParams0.zw; + OUT_uv1 = IN_uv * rtParams1.zw; + OUT_uv2 = IN_uv * rtParams2.zw; + OUT_uv3 = IN_uv * rtParams3.zw; + */ + + + OUT_hpos = IN_pos; + OUT_uv0 = viewportCoordToRenderTarget( IN_uv, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( IN_uv, rtParams1 ); + OUT_uv2 = viewportCoordToRenderTarget( IN_uv, rtParams2 ); + OUT_uv3 = viewportCoordToRenderTarget( IN_uv, rtParams3 ); + + + OUT_wsEyeRay = IN_wsEyeRay; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl new file mode 100644 index 000000000..ae94edd78 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// This vertex and pixel shader applies a 3 x 3 blur to the image in +// colorMapSampler, which is the same size as the render target. +// The sample weights are 1/16 in the corners, 2/16 on the edges, +// and 4/16 in the center. + +#include "../../../gl/hlslCompat.glsl" + +uniform sampler2D colorSampler; // Output of DofNearCoc() + +in vec4 texCoords; +#define IN_texCoords texCoords + +out vec4 OUT_col; + +void main() +{ + vec4 color; + color = vec4(0.0); + color += texture( colorSampler, IN_texCoords.xz ); + color += texture( colorSampler, IN_texCoords.yz ); + color += texture( colorSampler, IN_texCoords.xw ); + color += texture( colorSampler, IN_texCoords.yw ); + OUT_col = color / 4.0; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl new file mode 100644 index 000000000..413abd352 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// This vertex and pixel shader applies a 3 x 3 blur to the image in +// colorMapSampler, which is the same size as the render target. +// The sample weights are 1/16 in the corners, 2/16 on the edges, +// and 4/16 in the center. + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +#define IN_position vPosition +#define IN_texCoords vTexCoord0 + +#define OUT_position gl_Position +out vec4 texCoords; +#define OUT_texCoords texCoords + +uniform vec2 oneOverTargetSize; +uniform vec4 rtParams0; + +void main() +{ + const vec4 halfPixel = vec4( -0.5, 0.5, -0.5, 0.5 ); + OUT_position = IN_position; //Transform_ObjectToClip( IN_position ); + + //vec2 uv = IN_texCoords + rtParams0.xy; + vec2 uv = viewportCoordToRenderTarget( IN_texCoords, rtParams0 ); + OUT_texCoords = uv.xxyy + halfPixel * oneOverTargetSize.xxyy; + + correctSSP(gl_Position); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl new file mode 100644 index 000000000..fbd529031 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(edgeBuffer); + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + return float4( TORQUE_TEX2D( edgeBuffer, IN.uv0 ).rrr, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAP.hlsl new file mode 100644 index 000000000..f5a71687d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAP.hlsl @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(edgeBuffer,0); +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 1); +uniform float2 targetSize; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float2 pixelSize = 1.0 / targetSize; + + // Sample edge buffer, bail if not on an edge + float edgeSample = TORQUE_TEX2D(edgeBuffer, IN.uv0).r; + clip(edgeSample - 1e-6); + + // Ok we're on an edge, so multi-tap sample, average, and return + float2 offsets[9] = { + float2( 0.0, 0.0), + float2(-1.0, -1.0), + float2( 0.0, -1.0), + float2( 1.0, -1.0), + float2( 1.0, 0.0), + float2( 1.0, 1.0), + float2( 0.0, 1.0), + float2(-1.0, 1.0), + float2(-1.0, 0.0), + }; + + float4 accumColor = 0; + for(int i = 0; i < 9; i++) + { + // Multiply the intensity of the edge, by the UV, so that things which maybe + // aren't quite full edges get sub-pixel sampling to reduce artifacts + + // Scaling offsets by 0.5 to reduce the range bluriness from extending to + // far outward from the edge. + + float2 offsetUV = IN.uv1 + edgeSample * ( offsets[i] * 0.5 ) * pixelSize;//rtWidthHeightInvWidthNegHeight.zw; + //offsetUV *= 0.999; + accumColor += TORQUE_TEX2D(backBuffer, offsetUV); + } + accumColor /= 9.0; + + return accumColor; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAV.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAV.hlsl new file mode 100644 index 000000000..4718b40f5 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeAAV.hlsl @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + + +uniform float4 rtParams0; +uniform float4 rtParams1; +uniform float4 rtParams2; +uniform float4 rtParams3; + +PFXVertToPix main( PFXVert IN ) +{ + PFXVertToPix OUT; + + OUT.hpos = IN.pos; + OUT.uv0 = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( IN.uv, rtParams1 ); + OUT.uv2 = viewportCoordToRenderTarget( IN.uv, rtParams2 ); + OUT.uv3 = viewportCoordToRenderTarget( IN.uv, rtParams3 ); + + OUT.wsEyeRay = IN.wsEyeRay; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeDetectP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeDetectP.hlsl new file mode 100644 index 000000000..2277126a8 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/edgeDetectP.hlsl @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prepassBuffer,0); + +// GPU Gems 3, pg 443-444 +float GetEdgeWeight(float2 uv0, in float2 targetSize) +{ + float2 offsets[9] = { + float2( 0.0, 0.0), + float2(-1.0, -1.0), + float2( 0.0, -1.0), + float2( 1.0, -1.0), + float2( 1.0, 0.0), + float2( 1.0, 1.0), + float2( 0.0, 1.0), + float2(-1.0, 1.0), + float2(-1.0, 0.0), + }; + + + float2 PixelSize = 1.0 / targetSize; + + float Depth[9]; + float3 Normal[9]; + + [unroll] //no getting around this, may as well save the annoying warning message + for(int i = 0; i < 9; i++) + { + float2 uv = uv0 + offsets[i] * PixelSize; + float4 gbSample = TORQUE_PREPASS_UNCONDITION( prepassBuffer, uv ); + Depth[i] = gbSample.a; + Normal[i] = gbSample.rgb; + } + + float4 Deltas1 = float4(Depth[1], Depth[2], Depth[3], Depth[4]); + float4 Deltas2 = float4(Depth[5], Depth[6], Depth[7], Depth[8]); + + Deltas1 = abs(Deltas1 - Depth[0]); + Deltas2 = abs(Depth[0] - Deltas2); + + float4 maxDeltas = max(Deltas1, Deltas2); + float4 minDeltas = max(min(Deltas1, Deltas2), 0.00001); + + float4 depthResults = step(minDeltas * 25.0, maxDeltas); + + Deltas1.x = dot(Normal[1], Normal[0]); + Deltas1.y = dot(Normal[2], Normal[0]); + Deltas1.z = dot(Normal[3], Normal[0]); + Deltas1.w = dot(Normal[4], Normal[0]); + + Deltas2.x = dot(Normal[5], Normal[0]); + Deltas2.y = dot(Normal[6], Normal[0]); + Deltas2.z = dot(Normal[7], Normal[0]); + Deltas2.w = dot(Normal[8], Normal[0]); + + Deltas1 = abs(Deltas1 - Deltas2); + + float4 normalResults = step(0.4, Deltas1); + + normalResults = max(normalResults, depthResults); + + return dot(normalResults, float4(1.0, 1.0, 1.0, 1.0)) * 0.25; +} + +uniform float2 targetSize; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + return GetEdgeWeight(IN.uv0, targetSize);//rtWidthHeightInvWidthNegHeight.zw); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl new file mode 100644 index 000000000..ccc3b8ba5 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec2 uv0; +#define IN_uv0 uv0 + +uniform sampler2D edgeBuffer; + +out vec4 OUT_col; + +void main() +{ + OUT_col = vec4( texture( edgeBuffer, IN_uv0 ).rrr, 1.0 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAP.glsl new file mode 100644 index 000000000..216dc8725 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAP.glsl @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D edgeBuffer; +uniform sampler2D backBuffer; +uniform vec2 targetSize; + +out vec4 OUT_col; + +void main() +{ + vec2 pixelSize = 1.0 / targetSize; + + // Sample edge buffer, bail if not on an edge + float edgeSample = texture(edgeBuffer, IN_uv0).r; + clip(edgeSample - 1e-6); + + // Ok we're on an edge, so multi-tap sample, average, and return + vec2 offsets[9] = vec2[]( + vec2( 0.0, 0.0), + vec2(-1.0, -1.0), + vec2( 0.0, -1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 0.0), + vec2( 1.0, 1.0), + vec2( 0.0, 1.0), + vec2(-1.0, 1.0), + vec2(-1.0, 0.0) + ); + + vec4 accumColor = vec4(0.0); + for(int i = 0; i < 9; i++) + { + // Multiply the intensity of the edge, by the UV, so that things which maybe + // aren't quite full edges get sub-pixel sampling to reduce artifacts + + // Scaling offsets by 0.5 to reduce the range bluriness from extending to + // far outward from the edge. + + vec2 offsetUV = IN_uv1 + edgeSample * ( offsets[i] * 0.5 ) * pixelSize;//rtWidthHeightInvWidthNegHeight.zw; + //offsetUV *= 0.999; + accumColor+= texture(backBuffer, offsetUV); + } + accumColor /= 9.0; + + OUT_col = accumColor; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAV.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAV.glsl new file mode 100644 index 000000000..975532272 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeAAV.glsl @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" +#include "../../gl/postFX.glsl" + +uniform vec4 rtParams0; +uniform vec4 rtParams1; +uniform vec4 rtParams2; +uniform vec4 rtParams3; + +void main() +{ + OUT_hpos = IN_pos; + OUT_uv0 = viewportCoordToRenderTarget( IN_uv, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( IN_uv, rtParams1 ); + OUT_uv2 = viewportCoordToRenderTarget( IN_uv, rtParams2 ); + OUT_uv3 = viewportCoordToRenderTarget( IN_uv, rtParams3 ); + + OUT_wsEyeRay = IN_wsEyeRay; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl new file mode 100644 index 000000000..d1856ecde --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +// GPU Gems 3, pg 443-444 +float GetEdgeWeight(vec2 uv0, in sampler2D prepassBuffer, in vec2 targetSize) +{ + vec2 offsets[9] = vec2[]( + vec2( 0.0, 0.0), + vec2(-1.0, -1.0), + vec2( 0.0, -1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 0.0), + vec2( 1.0, 1.0), + vec2( 0.0, 1.0), + vec2(-1.0, 1.0), + vec2(-1.0, 0.0) + ); + + + vec2 PixelSize = 1.0 / targetSize; + + float Depth[9]; + vec3 Normal[9]; + + for(int i = 0; i < 9; i++) + { + vec2 uv = uv0 + offsets[i] * PixelSize; + vec4 gbSample = prepassUncondition( prepassBuffer, uv ); + Depth[i] = gbSample.a; + Normal[i] = gbSample.rgb; + } + + vec4 Deltas1 = vec4(Depth[1], Depth[2], Depth[3], Depth[4]); + vec4 Deltas2 = vec4(Depth[5], Depth[6], Depth[7], Depth[8]); + + Deltas1 = abs(Deltas1 - Depth[0]); + Deltas2 = abs(Depth[0] - Deltas2); + + vec4 maxDeltas = max(Deltas1, Deltas2); + vec4 minDeltas = max(min(Deltas1, Deltas2), 0.00001); + + vec4 depthResults = step(minDeltas * 25.0, maxDeltas); + + Deltas1.x = dot(Normal[1], Normal[0]); + Deltas1.y = dot(Normal[2], Normal[0]); + Deltas1.z = dot(Normal[3], Normal[0]); + Deltas1.w = dot(Normal[4], Normal[0]); + + Deltas2.x = dot(Normal[5], Normal[0]); + Deltas2.y = dot(Normal[6], Normal[0]); + Deltas2.z = dot(Normal[7], Normal[0]); + Deltas2.w = dot(Normal[8], Normal[0]); + + Deltas1 = abs(Deltas1 - Deltas2); + + vec4 normalResults = step(0.4, Deltas1); + + normalResults = max(normalResults, depthResults); + + return dot(normalResults, vec4(1.0, 1.0, 1.0, 1.0)) * 0.25; +} + +in vec2 uv0; +#define IN_uv0 uv0 + +uniform sampler2D prepassBuffer; +uniform vec2 targetSize; + +out vec4 OUT_col; + +void main() +{ + OUT_col = vec4( GetEdgeWeight(IN_uv0, prepassBuffer, targetSize ) );//rtWidthHeightInvWidthNegHeight.zw); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/flashP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/flashP.hlsl new file mode 100644 index 000000000..93daf3c26 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/flashP.hlsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" +#include "../torque.hlsl" + +uniform float damageFlash; +uniform float whiteOut; +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); + +float4 main(PFXVertToPix IN) : TORQUE_TARGET0 +{ + float4 color1 = TORQUE_TEX2D(backBuffer, IN.uv0); + float4 color2 = color1 * MUL_COLOR; + float4 damage = lerp(color1,color2,damageFlash); + return lerp(damage,WHITE_COLOR,whiteOut); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fogP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/fogP.hlsl new file mode 100644 index 000000000..de5fd65dc --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fogP.hlsl @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +#include "./postFx.hlsl" +#include "./../torque.hlsl" +#include "./../shaderModelAutoGen.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); +uniform float3 eyePosWorld; +uniform float4 fogColor; +uniform float3 fogData; +uniform float4 rtParams0; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + //float2 prepassCoord = ( IN.uv0.xy * rtParams0.zw ) + rtParams0.xy; + float depth = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).w; + //return float4( depth, 0, 0, 0.7 ); + + float factor = computeSceneFog( eyePosWorld, + eyePosWorld + ( IN.wsEyeRay * depth ), + fogData.x, + fogData.y, + fogData.z ); + + return hdrEncode( float4( toLinear(fogColor.rgb), 1.0 - saturate( factor ) ) ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/Fxaa3_11.h b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/Fxaa3_11.h new file mode 100644 index 000000000..9ca7627d4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/Fxaa3_11.h @@ -0,0 +1,2047 @@ +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +/*============================================================================ + + INTEGRATION KNOBS + +============================================================================*/ +// +// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). +// FXAA_360_OPT is a prototype for the new optimized 360 version. +// +// 1 = Use API. +// 0 = Don't use API. +// +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PS3 + #define FXAA_PS3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360 + #define FXAA_360 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS +============================================================================*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + + + + +/*============================================================================ + + FXAA3 CONSOLE - PC VERSION + +------------------------------------------------------------------------------ +Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 +Or + #define FXAA_QUALITY__PRESET 20 +Either are higher qualilty and almost as fast as this on modern PC GPUs. +============================================================================*/ +#if (FXAA_PC_CONSOLE == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); +/*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + +------------------------------------------------------------------------------ +This optimized version thanks to suggestions from Andy Luedke. +Should be fully tex bound in all cases. +As of the FXAA 3.11 release, I have still not tested this code, +however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. +And note this is replacing the old unoptimized version. +If it does not work, please let me know so I can fix it. +============================================================================*/ +#if (FXAA_360 == 1) +/*--------------------------------------------------------------------------*/ +[reduceTempRegUsage(4)] +float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif +/*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); +/*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; +/*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); +/*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; +/*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; +/*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; +/*--------------------------------------------------------------------------*/ + float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + +============================================================================== +The code below does not exactly match the assembly. +I have a feeling that 12 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + +MEAN: 17% 61% 67% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + +MEAN: 30% 23% 61% 76% 100% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 13 cycles, 3 r regs, 923,076,923 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O3 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif +/*--------------------------------------------------------------------------*/ +// (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + +============================================================================== +The code mostly matches the assembly. +I have a feeling that 14 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). +Will look at fixing this for FXAA 3.12. +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + +MEAN: 26% 60% 56% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + +MEAN: 33% 33% 60% 86% 80% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 15 cycles, 3 r regs, 800,000,000 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O2 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); +/*--------------------------------------------------------------------------*/ +// (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif +/*--------------------------------------------------------------------------*/ +// (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; +/*--------------------------------------------------------------------------*/ +// (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (13) + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; +/*--------------------------------------------------------------------------*/ +// (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaP.hlsl new file mode 100644 index 000000000..269bfea67 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaP.hlsl @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModel.hlsl" + +#define FXAA_PC 1 +#if (TORQUE_SM <= 30) +#define FXAA_HLSL_3 1 +#elif TORQUE_SM < 49 +#define FXAA_HLSL_4 1 +#elif TORQUE_SM >=50 +#define FXAA_HLSL_5 1 +#endif +#define FXAA_QUALITY__PRESET 12 +#define FXAA_GREEN_AS_LUMA 1 + +#include "Fxaa3_11.h" + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(colorTex, 0); + +uniform float2 oneOverTargetSize; + + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ +#if (TORQUE_SM >= 10 && TORQUE_SM <=30) + FxaaTex tex = colorTex; +#elif TORQUE_SM >=40 + FxaaTex tex; + tex.smpl = colorTex; + tex.tex = texture_colorTex; +#endif + + return FxaaPixelShader( + + IN.uv0, // vertex position + + 0, // Unused... console stuff + + tex, // The color back buffer + + tex, // Used for 360 optimization + + tex, // Used for 360 optimization + + oneOverTargetSize, + + 0, // Unused... console stuff + + 0, // Unused... console stuff + + 0, // Unused... console stuff + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + 0.75, + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + 0.166, + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + 0, + + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + 8, + + 0, // Unused... console stuff + + 0, // Unused... console stuff + + 0 // Unused... console stuff + + ); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaV.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaV.hlsl new file mode 100644 index 000000000..3bef0a4d3 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/fxaaV.hlsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../../torque.hlsl" +#include "./../postFX.hlsl" + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + float2 uv0 : TEXCOORD0; +}; + +uniform float4 rtParams0; + +VertToPix main( PFXVert IN ) +{ + VertToPix OUT; + + OUT.hpos = float4(IN.pos,1); + OUT.uv0 = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaP.glsl new file mode 100644 index 000000000..19d76ef42 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaP.glsl @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define FXAA_PC 1 +#define FXAA_GLSL_130 1 +#define FXAA_QUALITY__PRESET 12 +#define FXAA_GREEN_AS_LUMA 1 + +#include "../Fxaa3_11.h" +#include "../../../gl/hlslCompat.glsl" + +uniform sampler2D colorTex ; +uniform vec2 oneOverTargetSize; + +in vec4 hpos; +in vec2 uv0; + +out vec4 OUT_col; + +void main() +{ + OUT_col = FxaaPixelShader( + + uv0, // vertex position + + vec4(0), // Unused... console stuff + + colorTex, // The color back buffer + + colorTex, // Used for 360 optimization + + colorTex, // Used for 360 optimization + + oneOverTargetSize, + + vec4(0), // Unused... console stuff + + vec4(0), // Unused... console stuff + + vec4(0), // Unused... console stuff + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + 0.75, + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + 0.166, + + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + 0, + + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + 8, + + 0, // Unused... console stuff + + 0, // Unused... console stuff + + vec4(0) // Unused... console stuff + + ); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaV.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaV.glsl new file mode 100644 index 000000000..55d445d91 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/fxaa/gl/fxaaV.glsl @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" +#include "../../../gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform vec4 rtParams0; + +out vec4 hpos; +out vec2 uv0; + +void main() +{ + gl_Position = vPosition; + hpos = gl_Position; + uv0 = viewportCoordToRenderTarget( vTexCoord0, rtParams0 ); + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gammaP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/gammaP.hlsl new file mode 100644 index 000000000..21b86fe4e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gammaP.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shadergen:/autogenConditioners.h" +#include "./postFx.hlsl" +#include "../torque.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); +TORQUE_UNIFORM_SAMPLER1D(colorCorrectionTex, 1); + +uniform float OneOverGamma; +uniform float Brightness; +uniform float Contrast; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 color = TORQUE_TEX2D(backBuffer, IN.uv0.xy); + + // Apply the color correction. + color.r = TORQUE_TEX1D( colorCorrectionTex, color.r ).r; + color.g = TORQUE_TEX1D( colorCorrectionTex, color.g ).g; + color.b = TORQUE_TEX1D( colorCorrectionTex, color.b ).b; + + // Apply gamma correction + color.rgb = pow( saturate(color.rgb), OneOverGamma ); + + // Apply contrast + color.rgb = ((color.rgb - 0.5f) * Contrast) + 0.5f; + + // Apply brightness + color.rgb += Brightness; + + return color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/VolFogGlowP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/VolFogGlowP.glsl new file mode 100644 index 000000000..01b072dd9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/VolFogGlowP.glsl @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 R.G.S. - Richards Game Studio, the Netherlands +// http://www.richardsgamestudio.com/ +// +// If you find this code useful or you are feeling particularly generous I +// would ask that you please go to http://www.richardsgamestudio.com/ then +// choose Donations from the menu on the left side and make a donation to +// Richards Game Studio. It will be highly appreciated. +// +// The MIT License: +// +// 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. +//----------------------------------------------------------------------------- + +// Volumetric Fog Glow postFx pixel shader V1.00 + +uniform sampler2D diffuseMap; +uniform float strength; + +out vec4 OUT_col; + +in vec2 uv0; +in vec2 uv1; +in vec2 uv2; +in vec2 uv3; + +in vec2 uv4; +in vec2 uv5; +in vec2 uv6; +in vec2 uv7; + +void main() +{ + vec4 kernel = vec4( 0.175, 0.275, 0.375, 0.475 ) * strength; + + OUT_col = vec4(0); + OUT_col += texture( diffuseMap, uv0 ) * kernel.x; + OUT_col += texture( diffuseMap, uv1 ) * kernel.y; + OUT_col += texture( diffuseMap, uv2 ) * kernel.z; + OUT_col += texture( diffuseMap, uv3 ) * kernel.w; + + OUT_col += texture( diffuseMap, uv4 ) * kernel.x; + OUT_col += texture( diffuseMap, uv5 ) * kernel.y; + OUT_col += texture( diffuseMap, uv6 ) * kernel.z; + OUT_col += texture( diffuseMap, uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + vec3 rgb2lum = vec3( 0.30, 0.59, 0.11 ); + OUT_col.a = dot( OUT_col.rgb, rgb2lum ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/chromaticLens.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/chromaticLens.glsl new file mode 100644 index 000000000..fdb85ba00 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/chromaticLens.glsl @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Based on 'Cubic Lens Distortion HLSL Shader' by François Tarlier +// www.francois-tarlier.com/blog/index.php/2009/11/cubic-lens-distortion-shader + +#include "./postFX.glsl" +#include "../../gl/torque.glsl" +#include "../../gl/hlslCompat.glsl" + +uniform sampler2D backBuffer; +uniform float distCoeff; +uniform float cubeDistort; +uniform vec3 colorDistort; + +out vec4 OUT_col; + +void main() +{ + vec2 tex = IN_uv0; + + float f = 0; + float r2 = (tex.x - 0.5) * (tex.x - 0.5) + (tex.y - 0.5) * (tex.y - 0.5); + + // Only compute the cubic distortion if necessary. + if ( cubeDistort == 0.0 ) + f = 1 + r2 * distCoeff; + else + f = 1 + r2 * (distCoeff + cubeDistort * sqrt(r2)); + + // Distort each color channel seperately to get a chromatic distortion effect. + vec3 outColor; + vec3 distort = vec3(f) + colorDistort; + + for ( int i=0; i < 3; i++ ) + { + float x = distort[i] * ( tex.x - 0.5 ) + 0.5; + float y = distort[i] * ( tex.y - 0.5 ) + 0.5; + outColor[i] = tex2Dlod( backBuffer, vec4(x,y,0,0) )[i]; + } + + OUT_col = vec4( outColor.rgb, 1 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/flashP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/flashP.glsl new file mode 100644 index 000000000..fc5072e6d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/flashP.glsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFX.glsl" +#include "../../gl/torque.glsl" +#include "../../gl/hlslCompat.glsl" + +uniform float damageFlash; +uniform float whiteOut; +uniform sampler2D backBuffer; + +out vec4 OUT_col; + +void main() +{ + vec4 color1 = texture(backBuffer, IN_uv0); + vec4 color2 = color1 * MUL_COLOR; + vec4 damage = mix(color1,color2,damageFlash); + OUT_col = mix(damage,WHITE_COLOR,whiteOut); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/fogP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/fogP.glsl new file mode 100644 index 000000000..dd16f8b46 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/fogP.glsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +#include "shadergen:/autogenConditioners.h" +#include "../../gl/torque.glsl" + +uniform sampler2D prepassTex ; +uniform vec3 eyePosWorld; +uniform vec4 fogColor; +uniform vec3 fogData; +uniform vec4 rtParams0; + +in vec2 uv0; +in vec3 wsEyeRay; + +out vec4 OUT_col; + +void main() +{ + //vec2 prepassCoord = ( uv0.xy * rtParams0.zw ) + rtParams0.xy; + float depth = prepassUncondition( prepassTex, uv0 ).w; + //return vec4( depth, 0, 0, 0.7 ); + + float factor = computeSceneFog( eyePosWorld, + eyePosWorld + ( wsEyeRay * depth ), + fogData.x, + fogData.y, + fogData.z ); + + OUT_col = hdrEncode( vec4( toLinear(fogColor.rgb), 1.0 - saturate( factor ) ) ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/gammaP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/gammaP.glsl new file mode 100644 index 000000000..a170bf39f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/gammaP.glsl @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "../../gl/torque.glsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D backBuffer; +uniform sampler1D colorCorrectionTex; + +uniform float OneOverGamma; +uniform float Brightness; +uniform float Contrast; + +in vec2 uv0; + +out vec4 OUT_col; + +void main() +{ + vec4 color = texture(backBuffer, uv0.xy); + + // Apply the color correction. + color.r = texture( colorCorrectionTex, color.r ).r; + color.g = texture( colorCorrectionTex, color.g ).g; + color.b = texture( colorCorrectionTex, color.b ).b; + + // Apply gamma correction + color.rgb = pow( clamp(color.rgb, vec3(0.0),vec3(1.0)), vec3(OneOverGamma) ); + + // Apply contrast + color.rgb = ((color.rgb - 0.5f) * Contrast) + 0.5f; + + // Apply brightness + color.rgb += Brightness; + + OUT_col = color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurP.glsl new file mode 100644 index 000000000..9ebca32fa --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurP.glsl @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +uniform sampler2D diffuseMap ; + +in vec4 hpos; //POSITION; +in vec2 uv0; //TEXCOORD0; +in vec2 uv1; //TEXCOORD1; +in vec2 uv2; //TEXCOORD2; +in vec2 uv3; //TEXCOORD3; +in vec2 uv4; //TEXCOORD4; +in vec2 uv5; //TEXCOORD5; +in vec2 uv6; //TEXCOORD6; +in vec2 uv7; //TEXCOORD7; + +out vec4 OUT_col; + +void main() +{ + vec4 kernel = vec4( 0.175, 0.275, 0.375, 0.475 ) * 0.5f; + + OUT_col = vec4(0); + OUT_col += texture( diffuseMap, uv0 ) * kernel.x; + OUT_col += texture( diffuseMap, uv1 ) * kernel.y; + OUT_col += texture( diffuseMap, uv2 ) * kernel.z; + OUT_col += texture( diffuseMap, uv3 ) * kernel.w; + + OUT_col += texture( diffuseMap, uv4 ) * kernel.x; + OUT_col += texture( diffuseMap, uv5 ) * kernel.y; + OUT_col += texture( diffuseMap, uv6 ) * kernel.z; + OUT_col += texture( diffuseMap, uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + vec3 rgb2lum = vec3( 0.30, 0.59, 0.11 ); + OUT_col.a = dot( OUT_col.rgb, rgb2lum ); + +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurV.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurV.glsl new file mode 100644 index 000000000..70445d7fe --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/glowBlurV.glsl @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "../../gl/torque.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +uniform vec2 texSize0; + +out vec4 hpos; //POSITION; +out vec2 uv0; //TEXCOORD0; +out vec2 uv1; //TEXCOORD1; +out vec2 uv2; //TEXCOORD2; +out vec2 uv3; //TEXCOORD3; +out vec2 uv4; //TEXCOORD4; +out vec2 uv5; //TEXCOORD5; +out vec2 uv6; //TEXCOORD6; +out vec2 uv7; //TEXCOORD7; + +void main() +{ + gl_Position = vPosition; + hpos = gl_Position; + + vec2 uv = vTexCoord0 + (0.5f / texSize0); + + uv0 = uv + ( ( BLUR_DIR * 3.5f ) / texSize0 ); + uv1 = uv + ( ( BLUR_DIR * 2.5f ) / texSize0 ); + uv2 = uv + ( ( BLUR_DIR * 1.5f ) / texSize0 ); + uv3 = uv + ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + uv4 = uv - ( ( BLUR_DIR * 3.5f ) / texSize0 ); + uv5 = uv - ( ( BLUR_DIR * 2.5f ) / texSize0 ); + uv6 = uv - ( ( BLUR_DIR * 1.5f ) / texSize0 ); + uv7 = uv - ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/gl/motionBlurP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/gl/motionBlurP.glsl new file mode 100644 index 000000000..56333e776 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/gl/motionBlurP.glsl @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "../../gl/torque.glsl" +#include "shadergen:/autogenConditioners.h" +#include "postFX.glsl" + +#undef IN_uv0 +#define _IN_uv0 uv0 + +uniform mat4 matPrevScreenToWorld; +uniform mat4 matWorldToScreen; + +// Passed in from setShaderConsts() +uniform float velocityMultiplier; + +uniform sampler2D backBuffer; +uniform sampler2D prepassTex; + +out vec4 OUT_col; + +void main() +{ + vec2 IN_uv0 = _IN_uv0; + float samples = 5; + + // First get the prepass texture for uv channel 0 + vec4 prepass = prepassUncondition( prepassTex, IN_uv0 ); + + // Next extract the depth + float depth = prepass.a; + + // Create the screen position + vec4 screenPos = vec4(IN_uv0.x*2-1, IN_uv0.y*2-1, depth*2-1, 1); + + // Calculate the world position + vec4 D = tMul(screenPos, matWorldToScreen); + vec4 worldPos = D / D.w; + + // Now calculate the previous screen position + vec4 previousPos = tMul( worldPos, matPrevScreenToWorld ); + previousPos /= previousPos.w; + + // Calculate the XY velocity + vec2 velocity = ((screenPos - previousPos) / velocityMultiplier).xy; + + // Generate the motion blur + vec4 color = texture(backBuffer, IN_uv0); + IN_uv0 += velocity; + + for(int i = 1; i 0 ) + { + rayStart.z -= ( startSide ); + //return vec4( 1, 0, 0, 1 ); + } + + vec3 hitPos; + vec3 ray = rayEnd - rayStart; + float rayLen = length( ray ); + vec3 rayDir = normalize( ray ); + + float endSide = dot( plane.xyz, rayEnd ) + plane.w; + float planeDist; + + if ( endSide < -0.005 ) + { + //return vec4( 0, 0, 1, 1 ); + hitPos = rayEnd; + planeDist = endSide; + } + else + { + //return vec4( 0, 0, 0, 0 ); + float den = dot( ray, plane.xyz ); + + // Parallal to the plane, return the endPnt. + //if ( den == 0.0f ) + // return endPnt; + + float dist = -( dot( plane.xyz, rayStart ) + plane.w ) / den; + if ( dist < 0.0 ) + dist = 0.0; + //return vec4( 1, 0, 0, 1 ); + //return vec4( ( dist ).rrr, 1 ); + + + hitPos = mix( rayStart, rayEnd, dist ); + + planeDist = dist; + } + + float delta = length( hitPos - rayStart ); + + float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * ( delta - FOG_DENSITY_OFFSET ) ) ); + //return vec4( fogAmt.rrr, 1 ); + + // Calculate the water "base" color based on depth. + vec4 fogColor = waterColor * texture( waterDepthGradMap, saturate( delta / waterDepthGradMax ) ); + // Modulate baseColor by the ambientColor. + fogColor *= vec4( ambientColor.rgb, 1 ); + + vec3 inColor = hdrDecode( texture( backbuffer, IN_uv0 ).rgb ); + inColor.rgb *= 1.0 - saturate( abs( planeDist ) / WET_DEPTH ) * WET_DARKENING; + //return vec4( inColor, 1 ); + + vec3 outColor = mix( inColor, fogColor.rgb, fogAmt ); + + OUT_col = vec4( hdrEncode( outColor ), 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurP.hlsl new file mode 100644 index 000000000..80f8ed02d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurP.hlsl @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ + float4 kernel = float4( 0.175, 0.275, 0.375, 0.475 ) * 0.5f; + + float4 OUT = 0; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv0 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv1 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv2 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv3 ) * kernel.w; + + OUT += TORQUE_TEX2D( diffuseMap, IN.uv4 ) * kernel.x; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv5 ) * kernel.y; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv6 ) * kernel.z; + OUT += TORQUE_TEX2D( diffuseMap, IN.uv7 ) * kernel.w; + + // Calculate a lumenance value in the alpha so we + // can use alpha test to save fillrate. + float3 rgb2lum = float3( 0.30, 0.59, 0.11 ); + OUT.a = dot( OUT.rgb, rgb2lum ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurV.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurV.hlsl new file mode 100644 index 000000000..b8f5cf9c2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/glowBlurV.hlsl @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" +#include "./../torque.hlsl" + + +uniform float2 texSize0; + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float2 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +VertToPix main( PFXVert IN ) +{ + VertToPix OUT; + + OUT.hpos = float4(IN.pos,1.0); + + float2 uv = IN.uv + (0.5f / texSize0); + + OUT.uv0 = uv + ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT.uv1 = uv + ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT.uv2 = uv + ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT.uv3 = uv + ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + OUT.uv4 = uv - ( ( BLUR_DIR * 3.5f ) / texSize0 ); + OUT.uv5 = uv - ( ( BLUR_DIR * 2.5f ) / texSize0 ); + OUT.uv6 = uv - ( ( BLUR_DIR * 1.5f ) / texSize0 ); + OUT.uv7 = uv - ( ( BLUR_DIR * 0.5f ) / texSize0 ); + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl new file mode 100644 index 000000000..77f4b9915 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float2 oneOverTargetSize; +uniform float gaussMultiplier; +uniform float gaussMean; +uniform float gaussStdDev; + +#define PI 3.141592654 + +float computeGaussianValue( float x, float mean, float std_deviation ) +{ + // The gaussian equation is defined as such: + /* + -(x - mean)^2 + ------------- + 1.0 2*std_dev^2 + f(x,mean,std_dev) = -------------------- * e^ + sqrt(2*pi*std_dev^2) + + */ + + float tmp = ( 1.0f / sqrt( 2.0f * PI * std_deviation * std_deviation ) ); + float tmp2 = exp( ( -( ( x - mean ) * ( x - mean ) ) ) / ( 2.0f * std_deviation * std_deviation ) ); + return tmp * tmp2; +} + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 color = { 0.0f, 0.0f, 0.0f, 0.0f }; + float offset = 0; + float weight = 0; + float x = 0; + float fI = 0; + + for( int i = 0; i < 9; i++ ) + { + fI = (float)i; + offset = (i - 4.0) * oneOverTargetSize.x; + x = (i - 4.0) / 4.0; + weight = gaussMultiplier * computeGaussianValue( x, gaussMean, gaussStdDev ); + color += (TORQUE_TEX2D( inputTex, IN.uv0 + float2( offset, 0.0f ) ) * weight ); + } + + return float4( color.rgb, 1.0f ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl new file mode 100644 index 000000000..8381f6a5d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float2 oneOverTargetSize; +uniform float gaussMultiplier; +uniform float gaussMean; +uniform float gaussStdDev; + +#define D3DX_PI 3.141592654 + +float computeGaussianValue( float x, float mean, float std_deviation ) +{ + // The gaussian equation is defined as such: + /* + -(x - mean)^2 + ------------- + 1.0 2*std_dev^2 + f(x,mean,std_dev) = -------------------- * e^ + sqrt(2*pi*std_dev^2) + + */ + float tmp = ( 1.0f / sqrt( 2.0f * D3DX_PI * std_deviation * std_deviation ) ); + float tmp2 = exp( ( -( ( x - mean ) * ( x - mean ) ) ) / ( 2.0f * std_deviation * std_deviation ) ); + return tmp * tmp2; +} + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 color = { 0.0f, 0.0f, 0.0f, 0.0f }; + float offset = 0; + float weight = 0; + float x = 0; + float fI = 0; + + for( int i = 0; i < 9; i++ ) + { + fI = (float)i; + offset = (fI - 4.0) * oneOverTargetSize.y; + x = (fI - 4.0) / 4.0; + weight = gaussMultiplier * computeGaussianValue( x, gaussMean, gaussStdDev ); + color += (TORQUE_TEX2D( inputTex, IN.uv0 + float2( 0.0f, offset ) ) * weight ); + } + + return float4( color.rgb, 1.0f ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/brightPassFilterP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/brightPassFilterP.hlsl new file mode 100644 index 000000000..9a8a93e97 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/brightPassFilterP.hlsl @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "../../torque.hlsl" + + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +TORQUE_UNIFORM_SAMPLER2D(luminanceTex, 1); +uniform float2 oneOverTargetSize; +uniform float brightPassThreshold; +uniform float g_fMiddleGray; + +static const float3 LUMINANCE_VECTOR = float3(0.3125f, 0.6154f, 0.0721f); + + +static float2 gTapOffsets[4] = +{ + { -0.5, 0.5 }, { 0.5, -0.5 }, + { -0.5, -0.5 }, { 0.5, 0.5 } +}; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 average = { 0.0f, 0.0f, 0.0f, 0.0f }; + + // Combine and average 4 samples from the source HDR texture. + for( int i = 0; i < 4; i++ ) + average += hdrDecode( TORQUE_TEX2D( inputTex, IN.uv0 + ( gTapOffsets[i] * oneOverTargetSize ) ) ); + average *= 0.25f; + + // Determine the brightness of this particular pixel. + float adaptedLum = TORQUE_TEX2D( luminanceTex, float2( 0.5f, 0.5f ) ).r; + float lum = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( average.rgb ); + //float lum = hdrLuminance( average.rgb ); + + // Determine whether this pixel passes the test... + if ( lum < brightPassThreshold ) + average = float4( 0.0f, 0.0f, 0.0f, 1.0f ); + + // Write the colour to the bright-pass render target + return hdrEncode( average ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl new file mode 100644 index 000000000..0f895070a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(currLum, 0); +TORQUE_UNIFORM_SAMPLER2D(lastAdaptedLum, 1); + +uniform float adaptRate; +uniform float deltaTime; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float fAdaptedLum = TORQUE_TEX2D( lastAdaptedLum, float2(0.5f, 0.5f) ).r; + float fCurrentLum = TORQUE_TEX2D( currLum, float2(0.5f, 0.5f) ).r; + + // The user's adapted luminance level is simulated by closing the gap between + // adapted luminance and current luminance by 2% every frame, based on a + // 30 fps rate. This is not an accurate model of human adaptation, which can + // take longer than half an hour. + float diff = fCurrentLum - fAdaptedLum; + float fNewAdaptation = fAdaptedLum + ( diff * ( 1.0 - exp( -deltaTime * adaptRate ) ) ); + + return float4( fNewAdaptation, 0.0, 0.0, 1.0f ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4P.hlsl new file mode 100644 index 000000000..01998af0b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4P.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "../../shdrConsts.h" +#include "../postFx.hlsl" + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +struct VertIn +{ + float4 hpos : TORQUE_POSITION; + float4 texCoords[8] : TEXCOORD0; +}; + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +float4 main( VertIn IN) : TORQUE_TARGET0 +{ + // We calculate the texture coords + // in the vertex shader as an optimization. + float4 sample = 0.0f; + for ( int i = 0; i < 8; i++ ) + { + sample += TORQUE_TEX2D( inputTex, IN.texCoords[i].xy ); + sample += TORQUE_TEX2D( inputTex, IN.texCoords[i].zw ); + } + + return sample / 16; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4V.hlsl new file mode 100644 index 000000000..c9a34b7f4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/downScale4x4V.hlsl @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "../../shdrConsts.h" +#include "../postFx.hlsl" +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 texCoords[8] : TEXCOORD0; +}; + +uniform float2 targetSize; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Conn main( PFXVert In ) +{ + Conn Out; + + Out.hpos = float4(In.pos,1.0); + + // Sample from the 16 surrounding points. Since the center point will be in + // the exact center of 16 texels, a 0.5f offset is needed to specify a texel + // center. + float2 texSize = float2( 1.0 / (targetSize.x - 1.0), 1.0 / (targetSize.y - 1.0) ); + + float4 uv; + uv.xy = In.uv.xy; + uv.zw = In.uv.xy; + + Out.texCoords[0] = uv; + Out.texCoords[0].x += texSize.x; + Out.texCoords[0].y += texSize.y; + Out.texCoords[0].z += texSize.x; + Out.texCoords[0].w += texSize.y; + Out.texCoords[0].x += ( 0 - 1.5 ) * texSize.x; + Out.texCoords[0].y += ( 0 - 1.5 ) * texSize.y; + Out.texCoords[0].z += ( 1 - 1.5 ) * texSize.x; + Out.texCoords[0].w += ( 0 - 1.5 ) * texSize.y; + + Out.texCoords[1] = uv; + Out.texCoords[1].x += texSize.x; + Out.texCoords[1].y += texSize.y; + Out.texCoords[1].z += texSize.x; + Out.texCoords[1].w += texSize.y; + Out.texCoords[1].x += ( 2 - 1.5 ) * texSize.x; + Out.texCoords[1].y += ( 0 - 1.5 ) * texSize.y; + Out.texCoords[1].z += ( 3 - 1.5 ) * texSize.x; + Out.texCoords[1].w += ( 0 - 1.5 ) * texSize.y; + + Out.texCoords[2] = uv; + Out.texCoords[2].x += texSize.x; + Out.texCoords[2].y += texSize.y; + Out.texCoords[2].z += texSize.x; + Out.texCoords[2].w += texSize.y; + Out.texCoords[2].x += ( 0 - 1.5 ) * texSize.x; + Out.texCoords[2].y += ( 1 - 1.5 ) * texSize.y; + Out.texCoords[2].z += ( 1 - 1.5 ) * texSize.x; + Out.texCoords[2].w += ( 1 - 1.5 ) * texSize.y; + + Out.texCoords[3] = uv; + Out.texCoords[3].x += texSize.x; + Out.texCoords[3].y += texSize.y; + Out.texCoords[3].z += texSize.x; + Out.texCoords[3].w += texSize.y; + Out.texCoords[3].x += ( 2 - 1.5 ) * texSize.x; + Out.texCoords[3].y += ( 1 - 1.5 ) * texSize.y; + Out.texCoords[3].z += ( 3 - 1.5 ) * texSize.x; + Out.texCoords[3].w += ( 1 - 1.5 ) * texSize.y; + + Out.texCoords[4] = uv; + Out.texCoords[4].x += texSize.x; + Out.texCoords[4].y += texSize.y; + Out.texCoords[4].z += texSize.x; + Out.texCoords[4].w += texSize.y; + Out.texCoords[4].x += ( 0 - 1.5 ) * texSize.x; + Out.texCoords[4].y += ( 2 - 1.5 ) * texSize.y; + Out.texCoords[4].z += ( 1 - 1.5 ) * texSize.x; + Out.texCoords[4].w += ( 2 - 1.5 ) * texSize.y; + + Out.texCoords[5] = uv; + Out.texCoords[5].x += texSize.x; + Out.texCoords[5].y += texSize.y; + Out.texCoords[5].z += texSize.x; + Out.texCoords[5].w += texSize.y; + Out.texCoords[5].x += ( 2 - 1.5 ) * texSize.x; + Out.texCoords[5].y += ( 2 - 1.5 ) * texSize.y; + Out.texCoords[5].z += ( 3 - 1.5 ) * texSize.x; + Out.texCoords[5].w += ( 2 - 1.5 ) * texSize.y; + + Out.texCoords[6] = uv; + Out.texCoords[6].x += texSize.x; + Out.texCoords[6].y += texSize.y; + Out.texCoords[6].z += texSize.x; + Out.texCoords[6].w += texSize.y; + Out.texCoords[6].x += ( 0 - 1.5 ) * texSize.x; + Out.texCoords[6].y += ( 3 - 1.5 ) * texSize.y; + Out.texCoords[6].z += ( 1 - 1.5 ) * texSize.x; + Out.texCoords[6].w += ( 3 - 1.5 ) * texSize.y; + + Out.texCoords[7] = uv; + Out.texCoords[7].x += texSize.x; + Out.texCoords[7].y += texSize.y; + Out.texCoords[7].z += texSize.x; + Out.texCoords[7].w += texSize.y; + Out.texCoords[7].x += ( 2 - 1.5 ) * texSize.x; + Out.texCoords[7].y += ( 3 - 1.5 ) * texSize.y; + Out.texCoords[7].z += ( 3 - 1.5 ) * texSize.x; + Out.texCoords[7].w += ( 3 - 1.5 ) * texSize.y; + + return Out; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/finalPassCombineP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/finalPassCombineP.hlsl new file mode 100644 index 000000000..f87616a6e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/finalPassCombineP.hlsl @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../torque.hlsl" +#include "../postFx.hlsl" +#include "../../shaderModelAutoGen.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(sceneTex, 0); +TORQUE_UNIFORM_SAMPLER2D(luminanceTex, 1); +TORQUE_UNIFORM_SAMPLER2D(bloomTex, 2); +TORQUE_UNIFORM_SAMPLER1D(colorCorrectionTex, 3); + +uniform float2 texSize0; +uniform float2 texSize2; + +uniform float g_fEnableToneMapping; +uniform float g_fMiddleGray; +uniform float g_fWhiteCutoff; +uniform float g_fEnableBlueShift; + +uniform float3 g_fBlueShiftColor; +uniform float g_fBloomScale; +uniform float g_fOneOverGamma; +uniform float Brightness; +uniform float Contrast; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 sample = hdrDecode( TORQUE_TEX2D( sceneTex, IN.uv0 ) ); + float adaptedLum = TORQUE_TEX2D( luminanceTex, float2( 0.5f, 0.5f ) ).r; + float4 bloom = TORQUE_TEX2D( bloomTex, IN.uv0 ); + + // For very low light conditions, the rods will dominate the perception + // of light, and therefore color will be desaturated and shifted + // towards blue. + if ( g_fEnableBlueShift > 0.0f ) + { + const float3 LUMINANCE_VECTOR = float3(0.2125f, 0.7154f, 0.0721f); + + // Define a linear blending from -1.5 to 2.6 (log scale) which + // determines the lerp amount for blue shift + float coef = 1.0f - ( adaptedLum + 1.5 ) / 4.1; + coef = saturate( coef * g_fEnableBlueShift ); + + // Lerp between current color and blue, desaturated copy + float3 rodColor = dot( sample.rgb, LUMINANCE_VECTOR ) * g_fBlueShiftColor; + sample.rgb = lerp( sample.rgb, rodColor, coef ); + + rodColor = dot( bloom.rgb, LUMINANCE_VECTOR ) * g_fBlueShiftColor; + bloom.rgb = lerp( bloom.rgb, rodColor, coef ); + } + + // Add the bloom effect. + sample += g_fBloomScale * bloom; + + // Map the high range of color values into a range appropriate for + // display, taking into account the user's adaptation level, + // white point, and selected value for for middle gray. + if ( g_fEnableToneMapping > 0.0f ) + { + float Lp = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( sample.rgb ); + //float toneScalar = ( Lp * ( 1.0 + ( Lp / ( g_fWhiteCutoff ) ) ) ) / ( 1.0 + Lp ); + float toneScalar = Lp; + sample.rgb = lerp( sample.rgb, sample.rgb * toneScalar, g_fEnableToneMapping ); + } + + // Apply the color correction. + sample.r = TORQUE_TEX1D( colorCorrectionTex, sample.r ).r; + sample.g = TORQUE_TEX1D( colorCorrectionTex, sample.g ).g; + sample.b = TORQUE_TEX1D( colorCorrectionTex, sample.b ).b; + + // Apply gamma correction + sample.rgb = pow( saturate(sample.rgb), g_fOneOverGamma ); + + // Apply contrast + sample.rgb = ((sample.rgb - 0.5f) * Contrast) + 0.5f; + + // Apply brightness + sample.rgb += Brightness; + + return sample; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl new file mode 100644 index 000000000..1d9a2df3e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex ; +uniform vec2 oneOverTargetSize; +uniform float gaussMultiplier; +uniform float gaussMean; +uniform float gaussStdDev; + +out vec4 OUT_col; + +#define PI 3.141592654 + +float computeGaussianValue( float x, float mean, float std_deviation ) +{ + // The gaussian equation is defined as such: + /* + -(x - mean)^2 + ------------- + 1.0 2*std_dev^2 + f(x,mean,std_dev) = -------------------- * e^ + sqrt(2*pi*std_dev^2) + + */ + + float tmp = ( 1.0f / sqrt( 2.0f * PI * std_deviation * std_deviation ) ); + float tmp2 = exp( ( -( ( x - mean ) * ( x - mean ) ) ) / ( 2.0f * std_deviation * std_deviation ) ); + return tmp * tmp2; +} + +void main() +{ + vec4 color = vec4( 0.0f, 0.0f, 0.0f, 0.0f ); + float offset = 0; + float weight = 0; + float x = 0; + float fI = 0; + + for( int i = 0; i < 9; i++ ) + { + fI = float(i); + offset = (i - 4.0) * oneOverTargetSize.x; + x = (i - 4.0) / 4.0; + weight = gaussMultiplier * computeGaussianValue( x, gaussMean, gaussStdDev ); + color += (texture( inputTex, IN_uv0 + vec2( offset, 0.0f ) ) * weight ); + } + + OUT_col = vec4( color.rgb, 1.0f ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl new file mode 100644 index 000000000..68f34b164 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex ; +uniform vec2 oneOverTargetSize; +uniform float gaussMultiplier; +uniform float gaussMean; +uniform float gaussStdDev; + +out vec4 OUT_col; + +#define D3DX_PI 3.141592654 + +float computeGaussianValue( float x, float mean, float std_deviation ) +{ + // The gaussian equation is defined as such: + /* + -(x - mean)^2 + ------------- + 1.0 2*std_dev^2 + f(x,mean,std_dev) = -------------------- * e^ + sqrt(2*pi*std_dev^2) + + */ + float tmp = ( 1.0f / sqrt( 2.0f * D3DX_PI * std_deviation * std_deviation ) ); + float tmp2 = exp( ( -( ( x - mean ) * ( x - mean ) ) ) / ( 2.0f * std_deviation * std_deviation ) ); + return tmp * tmp2; +} + +void main() +{ + vec4 color = vec4( 0.0f, 0.0f, 0.0f, 0.0f ); + float offset = 0; + float weight = 0; + float x = 0; + float fI = 0; + + for( int i = 0; i < 9; i++ ) + { + fI = float(i); + offset = (fI - 4.0) * oneOverTargetSize.y; + x = (fI - 4.0) / 4.0; + weight = gaussMultiplier * computeGaussianValue( x, gaussMean, gaussStdDev ); + color += (texture( inputTex, IN_uv0 + vec2( 0.0f, offset ) ) * weight ); + } + + OUT_col = vec4( color.rgb, 1.0f ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/brightPassFilterP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/brightPassFilterP.glsl new file mode 100644 index 000000000..f220ca1e7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/brightPassFilterP.glsl @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex ; +uniform sampler2D luminanceTex ; +uniform vec2 oneOverTargetSize; +uniform float brightPassThreshold; +uniform float g_fMiddleGray; + +const vec3 LUMINANCE_VECTOR = vec3(0.3125f, 0.6154f, 0.0721f); + +out vec4 OUT_col; + + +const vec2 gTapOffsets[4] = vec2[] +( + vec2( -0.5, 0.5 ), vec2( 0.5, -0.5 ), + vec2( -0.5, -0.5 ), vec2( 0.5, 0.5 ) +); + +void main() +{ + vec4 average = vec4( 0.0f, 0.0f, 0.0f, 0.0f ); + + // Combine and average 4 samples from the source HDR texture. + for( int i = 0; i < 4; i++ ) + average += hdrDecode( texture( inputTex, IN_uv0 + ( gTapOffsets[i] * oneOverTargetSize ) ) ); + average *= 0.25f; + + // Determine the brightness of this particular pixel. + float adaptedLum = texture( luminanceTex, vec2( 0.5f, 0.5f ) ).r; + float lum = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( average.rgb ); + //float lum = hdrLuminance( average.rgb ); + + // Determine whether this pixel passes the test... + if ( lum < brightPassThreshold ) + average = vec4( 0.0f, 0.0f, 0.0f, 1.0f ); + + // Write the colour to the bright-pass render target + OUT_col = hdrEncode( average ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl new file mode 100644 index 000000000..96ee9d6df --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D currLum; +uniform sampler2D lastAdaptedLum; + +uniform float adaptRate; +uniform float deltaTime; + +out vec4 OUT_col; + +void main() +{ + float fAdaptedLum = texture( lastAdaptedLum, vec2(0.5f, 0.5f) ).r; + float fCurrentLum = texture( currLum, vec2(0.5f, 0.5f) ).r; + + // The user's adapted luminance level is simulated by closing the gap between + // adapted luminance and current luminance by 2% every frame, based on a + // 30 fps rate. This is not an accurate model of human adaptation, which can + // take longer than half an hour. + float diff = fCurrentLum - fAdaptedLum; + float fNewAdaptation = fAdaptedLum + ( diff * ( 1.0 - exp( -deltaTime * adaptRate ) ) ); + + OUT_col = vec4( fNewAdaptation, 0.0, 0.0, 1.0f ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4P.glsl new file mode 100644 index 000000000..131671760 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4P.glsl @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_GLSL +#include "../../../shdrConsts.h" +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec4 texCoords[8]; +#define IN_texCoords texCoords + +uniform sampler2D inputTex; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + // We calculate the texture coords + // in the vertex shader as an optimization. + vec4 _sample = vec4(0.0f); + for ( int i = 0; i < 8; i++ ) + { + _sample += texture( inputTex, IN_texCoords[i].xy ); + _sample += texture( inputTex, IN_texCoords[i].zw ); + } + + OUT_col = _sample / 16; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4V.glsl new file mode 100644 index 000000000..51f1da896 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/downScale4x4V.glsl @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_GLSL +#include "../../../shdrConsts.h" +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +#define In_pos vPosition +#define In_uv vTexCoord0 + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- +out vec4 texCoords[8]; +#define Out_texCoords texCoords + +#define Out_hpos gl_Position + +uniform vec2 targetSize; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + Out_hpos = In_pos; + + // Sample from the 16 surrounding points. Since the center point will be in + // the exact center of 16 texels, a 0.5f offset is needed to specify a texel + // center. + vec2 texSize = vec2( 1.0 / (targetSize.x - 1.0), 1.0 / (targetSize.y - 1.0) ); + + vec4 uv; + uv.xy = In_uv.xy; + uv.zw = In_uv.xy; + + Out_texCoords[0] = uv; + Out_texCoords[0].x += texSize.x; + Out_texCoords[0].y += texSize.y; + Out_texCoords[0].z += texSize.x; + Out_texCoords[0].w += texSize.y; + Out_texCoords[0].x += ( 0 - 1.5 ) * texSize.x; + Out_texCoords[0].y += ( 0 - 1.5 ) * texSize.y; + Out_texCoords[0].z += ( 1 - 1.5 ) * texSize.x; + Out_texCoords[0].w += ( 0 - 1.5 ) * texSize.y; + + Out_texCoords[1] = uv; + Out_texCoords[1].x += texSize.x; + Out_texCoords[1].y += texSize.y; + Out_texCoords[1].z += texSize.x; + Out_texCoords[1].w += texSize.y; + Out_texCoords[1].x += ( 2 - 1.5 ) * texSize.x; + Out_texCoords[1].y += ( 0 - 1.5 ) * texSize.y; + Out_texCoords[1].z += ( 3 - 1.5 ) * texSize.x; + Out_texCoords[1].w += ( 0 - 1.5 ) * texSize.y; + + Out_texCoords[2] = uv; + Out_texCoords[2].x += texSize.x; + Out_texCoords[2].y += texSize.y; + Out_texCoords[2].z += texSize.x; + Out_texCoords[2].w += texSize.y; + Out_texCoords[2].x += ( 0 - 1.5 ) * texSize.x; + Out_texCoords[2].y += ( 1 - 1.5 ) * texSize.y; + Out_texCoords[2].z += ( 1 - 1.5 ) * texSize.x; + Out_texCoords[2].w += ( 1 - 1.5 ) * texSize.y; + + Out_texCoords[3] = uv; + Out_texCoords[3].x += texSize.x; + Out_texCoords[3].y += texSize.y; + Out_texCoords[3].z += texSize.x; + Out_texCoords[3].w += texSize.y; + Out_texCoords[3].x += ( 2 - 1.5 ) * texSize.x; + Out_texCoords[3].y += ( 1 - 1.5 ) * texSize.y; + Out_texCoords[3].z += ( 3 - 1.5 ) * texSize.x; + Out_texCoords[3].w += ( 1 - 1.5 ) * texSize.y; + + Out_texCoords[4] = uv; + Out_texCoords[4].x += texSize.x; + Out_texCoords[4].y += texSize.y; + Out_texCoords[4].z += texSize.x; + Out_texCoords[4].w += texSize.y; + Out_texCoords[4].x += ( 0 - 1.5 ) * texSize.x; + Out_texCoords[4].y += ( 2 - 1.5 ) * texSize.y; + Out_texCoords[4].z += ( 1 - 1.5 ) * texSize.x; + Out_texCoords[4].w += ( 2 - 1.5 ) * texSize.y; + + Out_texCoords[5] = uv; + Out_texCoords[5].x += texSize.x; + Out_texCoords[5].y += texSize.y; + Out_texCoords[5].z += texSize.x; + Out_texCoords[5].w += texSize.y; + Out_texCoords[5].x += ( 2 - 1.5 ) * texSize.x; + Out_texCoords[5].y += ( 2 - 1.5 ) * texSize.y; + Out_texCoords[5].z += ( 3 - 1.5 ) * texSize.x; + Out_texCoords[5].w += ( 2 - 1.5 ) * texSize.y; + + Out_texCoords[6] = uv; + Out_texCoords[6].x += texSize.x; + Out_texCoords[6].y += texSize.y; + Out_texCoords[6].z += texSize.x; + Out_texCoords[6].w += texSize.y; + Out_texCoords[6].x += ( 0 - 1.5 ) * texSize.x; + Out_texCoords[6].y += ( 3 - 1.5 ) * texSize.y; + Out_texCoords[6].z += ( 1 - 1.5 ) * texSize.x; + Out_texCoords[6].w += ( 3 - 1.5 ) * texSize.y; + + Out_texCoords[7] = uv; + Out_texCoords[7].x += texSize.x; + Out_texCoords[7].y += texSize.y; + Out_texCoords[7].z += texSize.x; + Out_texCoords[7].w += texSize.y; + Out_texCoords[7].x += ( 2 - 1.5 ) * texSize.x; + Out_texCoords[7].y += ( 3 - 1.5 ) * texSize.y; + Out_texCoords[7].z += ( 3 - 1.5 ) * texSize.x; + Out_texCoords[7].w += ( 3 - 1.5 ) * texSize.y; + + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl new file mode 100644 index 000000000..8437cb04b --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/finalPassCombineP.glsl @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D sceneTex; +uniform sampler2D luminanceTex; +uniform sampler2D bloomTex; +uniform sampler1D colorCorrectionTex; + +uniform vec2 texSize0; +uniform vec2 texSize2; + +uniform float g_fEnableToneMapping; +uniform float g_fMiddleGray; +uniform float g_fWhiteCutoff; + +uniform float g_fEnableBlueShift; +uniform vec3 g_fBlueShiftColor; + +uniform float g_fBloomScale; + +uniform float g_fOneOverGamma; +uniform float Brightness; +uniform float Contrast; + +out vec4 OUT_col; + +void main() +{ + vec4 _sample = hdrDecode( texture( sceneTex, IN_uv0 ) ); + float adaptedLum = texture( luminanceTex, vec2( 0.5f, 0.5f ) ).r; + vec4 bloom = texture( bloomTex, IN_uv0 ); + + // For very low light conditions, the rods will dominate the perception + // of light, and therefore color will be desaturated and shifted + // towards blue. + if ( g_fEnableBlueShift > 0.0f ) + { + const vec3 LUMINANCE_VECTOR = vec3(0.2125f, 0.7154f, 0.0721f); + + // Define a linear blending from -1.5 to 2.6 (log scale) which + // determines the mix amount for blue shift + float coef = 1.0f - ( adaptedLum + 1.5 ) / 4.1; + coef = saturate( coef * g_fEnableBlueShift ); + + // Lerp between current color and blue, desaturated copy + vec3 rodColor = dot( _sample.rgb, LUMINANCE_VECTOR ) * g_fBlueShiftColor; + _sample.rgb = mix( _sample.rgb, rodColor, coef ); + + rodColor = dot( bloom.rgb, LUMINANCE_VECTOR ) * g_fBlueShiftColor; + bloom.rgb = mix( bloom.rgb, rodColor, coef ); + } + + // Add the bloom effect. + _sample += g_fBloomScale * bloom; + + // Map the high range of color values into a range appropriate for + // display, taking into account the user's adaptation level, + // white point, and selected value for for middle gray. + if ( g_fEnableToneMapping > 0.0f ) + { + float Lp = (g_fMiddleGray / (adaptedLum + 0.0001)) * hdrLuminance( _sample.rgb ); + //float toneScalar = ( Lp * ( 1.0 + ( Lp / ( g_fWhiteCutoff ) ) ) ) / ( 1.0 + Lp ); + float toneScalar = Lp; + _sample.rgb = mix( _sample.rgb, _sample.rgb * toneScalar, g_fEnableToneMapping ); + } + + // Apply the color correction. + _sample.r = texture( colorCorrectionTex, _sample.r ).r; + _sample.g = texture( colorCorrectionTex, _sample.g ).g; + _sample.b = texture( colorCorrectionTex, _sample.b ).b; + + // Apply gamma correction + _sample.rgb = pow( _sample.rgb, vec3(g_fOneOverGamma) ); + + // Apply contrast + _sample.rgb = ((_sample.rgb - 0.5f) * Contrast) + 0.5f; + + // Apply brightness + _sample.rgb += Brightness; + + OUT_col = _sample; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/luminanceVisP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/luminanceVisP.glsl new file mode 100644 index 000000000..ee9c28c87 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/luminanceVisP.glsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex; +uniform float brightPassThreshold; + +out vec4 OUT_col; + +void main() +{ + vec4 _sample = hdrDecode( texture( inputTex, IN_uv0 ) ); + + // Determine the brightness of this particular pixel. + float lum = hdrLuminance( _sample.rgb ); + + // Write the colour to the bright-pass render target + OUT_col = ( vec4( lum.rrr, 1 ) ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl new file mode 100644 index 000000000..8a2b9b318 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex; +uniform vec2 texSize0; + +uniform float g_fMinLuminace; + +out vec4 OUT_col; + +const vec2 gTapOffsets[9] = vec2[] +( + vec2( -1.0, -1.0 ), vec2( 0.0, -1.0 ), vec2( 1.0, -1.0 ), + vec2( -1.0, 0.0 ), vec2( 0.0, 0.0 ), vec2( 1.0, 0.0 ), + vec2( -1.0, 1.0 ), vec2( 0.0, 1.0 ), vec2( 1.0, 1.0 ) +); + + +void main() +{ + vec2 tsize = 1.0 / texSize0; + + vec3 _sample; + float average = 0.0; + + for ( int i = 0; i < 9; i++ ) + { + // Decode the hdr value. + _sample = hdrDecode( texture( inputTex, IN_uv0 + ( gTapOffsets[i] * tsize ) ).rgb ); + + // Get the luminance and add it to the average. + float lum = max( hdrLuminance( _sample ), g_fMinLuminace ); + average += log( lum ); + } + + average = exp( average / 9.0 ); + + OUT_col = vec4( average, 0.0, 0.0, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl new file mode 100644 index 000000000..2e800d612 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" + +uniform sampler2D inputTex; +uniform vec2 oneOverTargetSize; + +out vec4 OUT_col; + +const vec2 gTapOffsets[16] = vec2[] +( + vec2( -1.5, -1.5 ), vec2( -0.5, -1.5 ), vec2( 0.5, -1.5 ), vec2( 1.5, -1.5 ), + vec2( -1.5, -0.5 ), vec2( -0.5, -0.5 ), vec2( 0.5, -0.5 ), vec2( 1.5, -0.5 ), + vec2( -1.5, 0.5 ), vec2( -0.5, 0.5 ), vec2( 0.5, 0.5 ), vec2( 1.5, 0.5 ), + vec2( -1.5, 1.5 ), vec2( -0.5, 1.5 ), vec2( 0.5, 1.5 ), vec2( 1.5, 1.5 ) +); + +void main() +{ + vec2 pixelSize = oneOverTargetSize; + + float average = 0.0; + + for ( int i = 0; i < 16; i++ ) + { + float lum = texture( inputTex, IN_uv0 + ( gTapOffsets[i] * pixelSize ) ).r; + average += lum; + } + + OUT_col = vec4( average / 16.0, 0.0, 0.0, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/luminanceVisP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/luminanceVisP.hlsl new file mode 100644 index 000000000..505d1b825 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/luminanceVisP.hlsl @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "../../torque.hlsl" + + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float brightPassThreshold; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 sample = hdrDecode( TORQUE_TEX2D( inputTex, IN.uv0 ) ); + + // Determine the brightness of this particular pixel. + float lum = hdrLuminance( sample.rgb ); + + // Write the colour to the bright-pass render target + return ( float4( lum.rrr, 1 ) ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumInitialP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumInitialP.hlsl new file mode 100644 index 000000000..2e23ece1f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumInitialP.hlsl @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../torque.hlsl" +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float2 texSize0; + +uniform float g_fMinLuminace; + +static float2 gTapOffsets[9] = +{ + { -1.0, -1.0 }, { 0.0, -1.0 }, { 1.0, -1.0 }, + { -1.0, 0.0 }, { 0.0, 0.0 }, { 1.0, 0.0 }, + { -1.0, 1.0 }, { 0.0, 1.0 }, { 1.0, 1.0 } +}; + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float2 tsize = 1.0 / texSize0; + + float3 sample; + float average = 0.0; + + for ( int i = 0; i < 9; i++ ) + { + // Decode the hdr value. + sample = hdrDecode( TORQUE_TEX2D( inputTex, IN.uv0 + ( gTapOffsets[i] * tsize ) ).rgb ); + + // Get the luminance and add it to the average. + float lum = max( hdrLuminance( sample ), g_fMinLuminace ); + average += log( lum ); + } + + average = exp( average / 9.0 ); + + return float4( average, 0.0, 0.0, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumIterativeP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumIterativeP.hlsl new file mode 100644 index 000000000..46ed6fc70 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/hdr/sampleLumIterativeP.hlsl @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float2 oneOverTargetSize; + + +static float2 gTapOffsets[16] = +{ + { -1.5, -1.5 }, { -0.5, -1.5 }, { 0.5, -1.5 }, { 1.5, -1.5 }, + { -1.5, -0.5 }, { -0.5, -0.5 }, { 0.5, -0.5 }, { 1.5, -0.5 }, + { -1.5, 0.5 }, { -0.5, 0.5 }, { 0.5, 0.5 }, { 1.5, 0.5 }, + { -1.5, 1.5 }, { -0.5, 1.5 }, { 0.5, 1.5 }, { 1.5, 1.5 } +}; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float2 pixelSize = oneOverTargetSize; + + float average = 0.0; + + for ( int i = 0; i < 16; i++ ) + { + float lum = TORQUE_TEX2D( inputTex, IN.uv0 + ( gTapOffsets[i] * pixelSize ) ).r; + average += lum; + } + + return float4( average / 16.0, 0.0, 0.0, 1.0 ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl new file mode 100644 index 000000000..a7917b328 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +uniform sampler2D backBuffer; // The original backbuffer. +uniform sampler2D prepassTex; // The pre-pass depth and normals. + +uniform float brightScalar; + +const vec3 LUMINANCE_VECTOR = vec3(0.3125f, 0.6154f, 0.0721f); + +out vec4 OUT_col; + +void main() +{ + vec4 col = vec4( 0, 0, 0, 1 ); + + // Get the depth at this pixel. + float depth = prepassUncondition( prepassTex, IN_uv0 ).w; + + // If the depth is equal to 1.0, read from the backbuffer + // and perform the exposure calculation on the result. + if ( depth >= 0.999 ) + { + col = texture( backBuffer, IN_uv0 ); + + //col = 1 - exp(-120000 * col); + col += dot( vec3(col), LUMINANCE_VECTOR ) + 0.0001f; + col *= brightScalar; + } + + OUT_col = col; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayP.glsl new file mode 100644 index 000000000..6d78f4eae --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/gl/lightRayP.glsl @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" + +uniform sampler2D frameSampler; +uniform sampler2D backBuffer; + +uniform vec3 camForward; +uniform vec3 lightDirection; +uniform vec2 screenSunPos; +uniform vec2 oneOverTargetSize; +uniform int numSamples; +uniform float density; +uniform float weight; +uniform float decay; +uniform float exposure; + +out vec4 OUT_col; + +void main() +{ + vec4 texCoord = vec4( IN_uv0.xy, 0, 0 ); + + // Store initial sample. + half3 color = half3(texture( frameSampler, texCoord.xy ).rgb); + + // Store original bb color. + vec4 bbCol = texture( backBuffer, IN_uv1 ); + + // Set up illumination decay factor. + half illuminationDecay = 1.0; + + float amount = saturate( dot( -lightDirection, camForward ) ); + + int samples = int(numSamples * amount); + + if ( samples <= 0 ) + { + OUT_col = bbCol; + return; + } + + // Calculate vector from pixel to light source in screen space. + half2 deltaTexCoord = half2( texCoord.xy - screenSunPos ); + + // Divide by number of samples and scale by control factor. + deltaTexCoord *= 1.0 / half(samples * density); + + // Evaluate summation from Equation 3 NUM_SAMPLES iterations. + for ( int i = 0; i < samples; i++ ) + { + // Step sample location along ray. + texCoord.xy -= deltaTexCoord; + + // Retrieve sample at new location. + half3 sample_ = half3(tex2Dlod( frameSampler, texCoord )); + + // Apply sample attenuation scale/decay factors. + sample_ *= illuminationDecay * weight; + + // Accumulate combined color. + color += sample_; + + // Update exponential decay factor. + illuminationDecay *= decay; + } + + //return saturate( amount ) * color * Exposure; + //return bbCol * decay; + + // Output final color with a further scale control factor. + OUT_col = saturate( amount ) * vec4( color * exposure, 1 ) + bbCol; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl new file mode 100644 index 000000000..b70bafa98 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../shaderModelAutoGen.hlsl" +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 1); + +uniform float brightScalar; + +static const float3 LUMINANCE_VECTOR = float3(0.3125f, 0.6154f, 0.0721f); + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 col = float4( 0, 0, 0, 1 ); + + // Get the depth at this pixel. + float depth = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).w; + + // If the depth is equal to 1.0, read from the backbuffer + // and perform the exposure calculation on the result. + if ( depth >= 0.999 ) + { + col = TORQUE_TEX2D( backBuffer, IN.uv0 ); + + //col = 1 - exp(-120000 * col); + col += dot( col.rgb, LUMINANCE_VECTOR ) + 0.0001f; + col *= brightScalar; + } + + return col; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayP.hlsl new file mode 100644 index 000000000..032894710 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/lightRay/lightRayP.hlsl @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(frameSampler, 0); +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 1); + + +uniform float3 camForward; +uniform int numSamples; +uniform float3 lightDirection; +uniform float density; +uniform float2 screenSunPos; +uniform float2 oneOverTargetSize; +uniform float weight; +uniform float decay; +uniform float exposure; + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float4 texCoord = float4( IN.uv0.xy, 0, 0 ); + + // Store initial sample. + half3 color = (half3)TORQUE_TEX2D( frameSampler, texCoord.xy ).rgb; + + // Store original bb color. + float4 bbCol = TORQUE_TEX2D( backBuffer, IN.uv1 ); + + // Set up illumination decay factor. + half illuminationDecay = 1.0; + + float amount = saturate( dot( -lightDirection, camForward ) ); + + int samples = numSamples * amount; + + if ( samples <= 0 ) + return bbCol; + + // Calculate vector from pixel to light source in screen space. + half2 deltaTexCoord = (half2)( texCoord.xy - screenSunPos ); + + // Divide by number of samples and scale by control factor. + deltaTexCoord *= (half)(1.0 / samples * density); + + // Evaluate summation from Equation 3 NUM_SAMPLES iterations. + for ( int i = 0; i < samples; i++ ) + { + // Step sample location along ray. + texCoord.xy -= deltaTexCoord; + + // Retrieve sample at new location. + half3 sample = (half3)TORQUE_TEX2DLOD( frameSampler, texCoord ); + + // Apply sample attenuation scale/decay factors. + sample *= half(illuminationDecay * weight); + + // Accumulate combined color. + color += sample; + + // Update exponential decay factor. + illuminationDecay *= half(decay); + } + + //return saturate( amount ) * color * Exposure; + //return bbCol * decay; + + // Output final color with a further scale control factor. + return saturate( amount ) * float4( color * exposure, 1 ) + bbCol; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl new file mode 100644 index 000000000..2c4777c36 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform sampler2D edgesMap : register(S0); +uniform sampler2D edgesMapL : register(S1); +uniform sampler2D areaMap : register(S2); + +#include "./functions.hlsl" + + +float4 main(float2 texcoord : TEXCOORD0) : COLOR0 +{ + float4 areas = 0.0; + + float2 e = tex2D(edgesMap, texcoord).rg; + + [branch] + if (e.g) // Edge at north + { + // Search distances to the left and to the right: + float2 d = float2(SearchXLeft(texcoord), SearchXRight(texcoord)); + + // Now fetch the crossing edges. Instead of sampling between edgels, we + // sample at -0.25, to be able to discern what value has each edgel: + float4 coords = mad(float4(d.x, -0.25, d.y + 1.0, -0.25), + PIXEL_SIZE.xyxy, texcoord.xyxy); + float e1 = tex2Dlevel0(edgesMapL, coords.xy).r; + float e2 = tex2Dlevel0(edgesMapL, coords.zw).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + areas.rg = Area(abs(d), e1, e2); + } + + [branch] + if (e.r) // Edge at west + { + // Search distances to the top and to the bottom: + float2 d = float2(SearchYUp(texcoord), SearchYDown(texcoord)); + + // Now fetch the crossing edges (yet again): + float4 coords = mad(float4(-0.25, d.x, -0.25, d.y + 1.0), + PIXEL_SIZE.xyxy, texcoord.xyxy); + float e1 = tex2Dlevel0(edgesMapL, coords.xy).g; + float e2 = tex2Dlevel0(edgesMapL, coords.zw).g; + + // Get the area for this direction: + areas.ba = Area(abs(d), e1, e2); + } + + return areas; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/edgeDetectionP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/edgeDetectionP.hlsl new file mode 100644 index 000000000..364bd948f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/edgeDetectionP.hlsl @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D colorMapG : register(S0); +uniform sampler2D prepassMap : register(S1); + +uniform float3 lumaCoefficients; +uniform float threshold; +uniform float depthThreshold; + + +float4 main( float2 texcoord : TEXCOORD0, + float4 offset[2]: TEXCOORD1) : COLOR0 +{ + // Luma calculation requires gamma-corrected colors (texture 'colorMapG'). + // + // Note that there is a lot of overlapped luma calculations; performance + // can be improved if this luma calculation is performed in the main pass, + // which may give you an edge if used in conjunction with a z prepass. + + float L = dot(tex2D(colorMapG, texcoord).rgb, lumaCoefficients); + + float Lleft = dot(tex2D(colorMapG, offset[0].xy).rgb, lumaCoefficients); + float Ltop = dot(tex2D(colorMapG, offset[0].zw).rgb, lumaCoefficients); + float Lright = dot(tex2D(colorMapG, offset[1].xy).rgb, lumaCoefficients); + float Lbottom = dot(tex2D(colorMapG, offset[1].zw).rgb, lumaCoefficients); + + float4 delta = abs(L.xxxx - float4(Lleft, Ltop, Lright, Lbottom)); + float4 edges = step(threshold, delta); + + // Add depth edges to color edges + float D = prepassUncondition(prepassMap, texcoord).w; + float Dleft = prepassUncondition(prepassMap, offset[0].xy).w; + float Dtop = prepassUncondition(prepassMap, offset[0].zw).w; + float Dright = prepassUncondition(prepassMap, offset[1].xy).w; + float Dbottom = prepassUncondition(prepassMap, offset[1].zw).w; + + delta = abs(D.xxxx - float4(Dleft, Dtop, Dright, Dbottom)); + edges += step(depthThreshold, delta); + + if (dot(edges, 1.0) == 0.0) + discard; + + return edges; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/functions.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/functions.hlsl new file mode 100644 index 000000000..9935a5e30 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/functions.hlsl @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform float2 texSize0; + +#if !defined(PIXEL_SIZE) +#define PIXEL_SIZE (1.0 / texSize0) +#define MAX_SEARCH_STEPS 8 +#define MAX_DISTANCE 33 +#endif + +// Typical Multiply-Add operation to ease translation to assembly code. + +float4 mad(float4 m, float4 a, float4 b) +{ + #if defined(XBOX) + float4 result; + asm { + mad result, m, a, b + }; + return result; + #else + return m * a + b; + #endif +} + + +// This one just returns the first level of a mip map chain, which allow us to +// avoid the nasty ddx/ddy warnings, even improving the performance a little +// bit. +float4 tex2Dlevel0(sampler2D map, float2 texcoord) +{ + return tex2Dlod(map, float4(texcoord, 0.0, 0.0)); +} + + +// Same as above, this eases translation to assembly code; +float4 tex2Doffset(sampler2D map, float2 texcoord, float2 offset) +{ + #if defined(XBOX) && MAX_SEARCH_STEPS < 6 + float4 result; + float x = offset.x; + float y = offset.y; + asm { + tfetch2D result, texcoord, map, OffsetX = x, OffsetY = y + }; + return result; + #else + return tex2Dlevel0(map, texcoord + PIXEL_SIZE * offset); + #endif +} + + +// Ok, we have the distance and both crossing edges, can you please return +// the float2 blending weights? +float2 Area(float2 distance, float e1, float e2) +{ + // * By dividing by areaSize - 1.0 below we are implicitely offsetting to + // always fall inside of a pixel + // * Rounding prevents bilinear access precision problems + float areaSize = MAX_DISTANCE * 5.0; + float2 pixcoord = MAX_DISTANCE * round(4.0 * float2(e1, e2)) + distance; + float2 texcoord = pixcoord / (areaSize - 1.0); + return tex2Dlevel0(areaMap, texcoord).rg; +} + + +// Search functions for the 2nd pass. +float SearchXLeft(float2 texcoord) +{ + // We compare with 0.9 to prevent bilinear access precision problems. + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, float2(i, 0.0)).g; + [flatten] if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchXRight(float2 texcoord) +{ + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, float2(i, 0.0)).g; + [flatten] if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchYUp(float2 texcoord) +{ + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, float2(i, 0.0).yx).r; + [flatten] if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchYDown(float2 texcoord) +{ + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, float2(i, 0.0).yx).r; + [flatten] if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl new file mode 100644 index 000000000..af01ce6f9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "../../../gl/hlslCompat.glsl" + +in vec2 texcoord; + +uniform sampler2D edgesMap; +uniform sampler2D edgesMapL; +uniform sampler2D areaMap; + +out vec4 OUT_col; + +#include "./functions.glsl" + + +void main() +{ + vec4 areas = vec4(0.0); + + vec2 e = texture(edgesMap, texcoord).rg; + + //[branch] + if (bool(e.g)) // Edge at north + { + // Search distances to the left and to the right: + vec2 d = vec2(SearchXLeft(texcoord), SearchXRight(texcoord)); + + // Now fetch the crossing edges. Instead of sampling between edgels, we + // sample at -0.25, to be able to discern what value has each edgel: + vec4 coords = mad(vec4(d.x, -0.25, d.y + 1.0, -0.25), + PIXEL_SIZE.xyxy, texcoord.xyxy); + float e1 = tex2Dlevel0(edgesMapL, coords.xy).r; + float e2 = tex2Dlevel0(edgesMapL, coords.zw).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + areas.rg = Area(abs(d), e1, e2); + } + + //[branch] + if (bool(e.r)) // Edge at west + { + // Search distances to the top and to the bottom: + vec2 d = vec2(SearchYUp(texcoord), SearchYDown(texcoord)); + + // Now fetch the crossing edges (yet again): + vec4 coords = mad(vec4(-0.25, d.x, -0.25, d.y + 1.0), + PIXEL_SIZE.xyxy, texcoord.xyxy); + float e1 = tex2Dlevel0(edgesMapL, coords.xy).g; + float e2 = tex2Dlevel0(edgesMapL, coords.zw).g; + + // Get the area for this direction: + areas.ba = Area(abs(d), e1, e2); + } + + OUT_col = areas; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl new file mode 100644 index 000000000..362f29b5c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D colorMapG; +uniform sampler2D prepassMap; + +uniform vec3 lumaCoefficients; +uniform float threshold; +uniform float depthThreshold; + +in vec2 texcoord; +in vec4 offset[2]; + +out vec4 OUT_col; + +void main() +{ + // Luma calculation requires gamma-corrected colors (texture 'colorMapG'). + // + // Note that there is a lot of overlapped luma calculations; performance + // can be improved if this luma calculation is performed in the main pass, + // which may give you an edge if used in conjunction with a z prepass. + + float L = dot(texture(colorMapG, texcoord).rgb, lumaCoefficients); + + float Lleft = dot(texture(colorMapG, offset[0].xy).rgb, lumaCoefficients); + float Ltop = dot(texture(colorMapG, offset[0].zw).rgb, lumaCoefficients); + float Lright = dot(texture(colorMapG, offset[1].xy).rgb, lumaCoefficients); + float Lbottom = dot(texture(colorMapG, offset[1].zw).rgb, lumaCoefficients); + + vec4 delta = abs(vec4(L) - vec4(Lleft, Ltop, Lright, Lbottom)); + vec4 edges = step(threshold, delta); + + // Add depth edges to color edges + float D = prepassUncondition(prepassMap, texcoord).w; + float Dleft = prepassUncondition(prepassMap, offset[0].xy).w; + float Dtop = prepassUncondition(prepassMap, offset[0].zw).w; + float Dright = prepassUncondition(prepassMap, offset[1].xy).w; + float Dbottom = prepassUncondition(prepassMap, offset[1].zw).w; + + delta = abs(vec4(D) - vec4(Dleft, Dtop, Dright, Dbottom)); + edges += step(depthThreshold, delta); + + if (dot(edges, vec4(1.0)) == 0.0) + discard; + + OUT_col = edges; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/functions.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/functions.glsl new file mode 100644 index 000000000..3ff56fb1a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/functions.glsl @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform vec2 texSize0; + +#if !defined(PIXEL_SIZE) +#define PIXEL_SIZE (1.0 / texSize0) +#define MAX_SEARCH_STEPS 8 +#define MAX_DISTANCE 33 +#endif + +// Typical Multiply-Add operation to ease translation to assembly code. + +vec4 mad(vec4 m, vec4 a, vec4 b) +{ + #if defined(XBOX) + vec4 result; + asm { + mad result, m, a, b + }; + return result; + #else + return m * a + b; + #endif +} + + +// This one just returns the first level of a mip map chain, which allow us to +// avoid the nasty ddx/ddy warnings, even improving the performance a little +// bit. +vec4 tex2Dlevel0(sampler2D map, vec2 texcoord) +{ + return tex2Dlod(map, vec4(texcoord, 0.0, 0.0)); +} + + +// Same as above, this eases translation to assembly code; +vec4 tex2Doffset(sampler2D map, vec2 texcoord, vec2 offset) +{ + #if defined(XBOX) && MAX_SEARCH_STEPS < 6 + vec4 result; + float x = offset.x; + float y = offset.y; + asm { + tfetch2D result, texcoord, map, OffsetX = x, OffsetY = y + }; + return result; + #else + return tex2Dlevel0(map, texcoord + PIXEL_SIZE * offset); + #endif +} + + +// Ok, we have the distance and both crossing edges, can you please return +// the vec2 blending weights? +vec2 Area(vec2 distance, float e1, float e2) +{ + // * By dividing by areaSize - 1.0 below we are implicitely offsetting to + // always fall inside of a pixel + // * Rounding prevents bilinear access precision problems + float areaSize = MAX_DISTANCE * 5.0; + vec2 pixcoord = MAX_DISTANCE * round(4.0 * vec2(e1, e2)) + distance; + vec2 texcoord = pixcoord / (areaSize - 1.0); + return tex2Dlevel0(areaMap, texcoord).rg; +} + + +// Search functions for the 2nd pass. +float SearchXLeft(vec2 texcoord) +{ + // We compare with 0.9 to prevent bilinear access precision problems. + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, vec2(i, 0.0)).g; + /*[flatten]*/ if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchXRight(vec2 texcoord) +{ + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, vec2(i, 0.0)).g; + /*[flatten]*/ if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchYUp(vec2 texcoord) +{ + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, vec2(i, 0.0).yx).r; + /*[flatten]*/ if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +// Search functions for the 2nd pass. +float SearchYDown(vec2 texcoord) +{ + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) + { + e = tex2Doffset(edgesMapL, texcoord, vec2(i, 0.0).yx).r; + /*[flatten]*/ if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl new file mode 100644 index 000000000..eddbcc47c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "../../../gl/hlslCompat.glsl" + +in vec2 texcoord; +in vec4 offset[2]; + +uniform sampler2D blendMap; +uniform sampler2D colorMapL; +uniform sampler2D colorMap; + +// Dummy sampers to please include. +uniform sampler2D areaMap; +uniform sampler2D edgesMapL; +#include "./functions.glsl" + +out vec4 OUT_col; + +void main() +{ + // Fetch the blending weights for current pixel: + vec4 topLeft = texture(blendMap, texcoord); + float bottom = texture(blendMap, offset[1].zw).g; + float right = texture(blendMap, offset[1].xy).a; + vec4 a = vec4(topLeft.r, bottom, topLeft.b, right); + + // Up to 4 lines can be crossing a pixel (one in each edge). So, we perform + // a weighted average, where the weight of each line is 'a' cubed, which + // favors blending and works well in practice. + vec4 w = a * a * a; + + // There is some blending weight with a value greater than 0.0? + float sum = dot(w, vec4(1.0)); + if (sum < 1e-5) + discard; + + vec4 color = vec4(0.0); + + // Add the contributions of the possible 4 lines that can cross this pixel: + #ifdef BILINEAR_FILTER_TRICK + vec4 coords = mad(vec4( 0.0, -a.r, 0.0, a.g), PIXEL_SIZE.yyyy, texcoord.xyxy); + color = mad(texture(colorMapL, coords.xy), vec4(w.r), color); + color = mad(texture(colorMapL, coords.zw), vec4(w.g), color); + + coords = mad(vec4(-a.b, 0.0, a.a, 0.0), PIXEL_SIZE.xxxx, texcoord.xyxy); + color = mad(texture(colorMapL, coords.xy), vec4(w.b), color); + color = mad(texture(colorMapL, coords.zw), vec4(w.a), color); + #else + vec4 C = texture(colorMap, texcoord); + vec4 Cleft = texture(colorMap, offset[0].xy); + vec4 Ctop = texture(colorMap, offset[0].zw); + vec4 Cright = texture(colorMap, offset[1].xy); + vec4 Cbottom = texture(colorMap, offset[1].zw); + color = mad(mix(C, Ctop, a.r), vec4(w.r), color); + color = mad(mix(C, Cbottom, a.g), vec4(w.g), color); + color = mad(mix(C, Cleft, a.b), vec4(w.b), color); + color = mad(mix(C, Cright, a.a), vec4(w.a), color); + #endif + + // Normalize the resulting color and we are finished! + OUT_col = color / sum; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/offsetV.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/offsetV.glsl new file mode 100644 index 000000000..53d927c29 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/offsetV.glsl @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +#define IN_position vPosition +#define IN_texcoord vTexCoord0 + +#define OUT_position gl_Position +out vec2 texcoord; +#define OUT_texcoord texcoord +out vec4 offset[2]; +#define OUT_offset offset + +uniform vec2 texSize0; + +void main() +{ + OUT_position = IN_position; + vec2 PIXEL_SIZE = 1.0 / texSize0; + + OUT_texcoord = IN_texcoord; + OUT_texcoord.xy += PIXEL_SIZE * 0.5; + + OUT_offset[0] = OUT_texcoord.xyxy + PIXEL_SIZE.xyxy * vec4(-1.0, 0.0, 0.0, -1.0); + OUT_offset[1] = OUT_texcoord.xyxy + PIXEL_SIZE.xyxy * vec4( 1.0, 0.0, 0.0, 1.0); + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/passthruV.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/passthruV.glsl new file mode 100644 index 000000000..1aa64112c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/gl/passthruV.glsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +#define IN_position vPosition +#define IN_texcoord vTexCoord0 + +#define OUT_position gl_Position +out vec2 texcoord; +#define OUT_texcoord texcoord + +uniform vec2 texSize0; + +void main() +{ + OUT_position = IN_position; + vec2 PIXEL_SIZE = 1.0 / texSize0; + + OUT_texcoord = IN_texcoord; + texcoord.xy += PIXEL_SIZE * 0.5; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl new file mode 100644 index 000000000..aaaacafe2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform sampler2D blendMap : register(S0); +uniform sampler2D colorMapL : register(S1); +uniform sampler2D colorMap : register(S2); + +// Dummy sampers to please include. +sampler2D areaMap; +sampler2D edgesMapL; +#include "./functions.hlsl" + + +float4 main( float2 texcoord : TEXCOORD0, + float4 offset[2]: TEXCOORD1 ) : COLOR0 +{ + // Fetch the blending weights for current pixel: + float4 topLeft = tex2D(blendMap, texcoord); + float bottom = tex2D(blendMap, offset[1].zw).g; + float right = tex2D(blendMap, offset[1].xy).a; + float4 a = float4(topLeft.r, bottom, topLeft.b, right); + + // Up to 4 lines can be crossing a pixel (one in each edge). So, we perform + // a weighted average, where the weight of each line is 'a' cubed, which + // favors blending and works well in practice. + float4 w = a * a * a; + + // There is some blending weight with a value greater than 0.0? + float sum = dot(w, 1.0); + if (sum < 1e-5) + discard; + + float4 color = 0.0; + + // Add the contributions of the possible 4 lines that can cross this pixel: + #ifdef BILINEAR_FILTER_TRICK + float4 coords = mad(float4( 0.0, -a.r, 0.0, a.g), PIXEL_SIZE.yyyy, texcoord.xyxy); + color = mad(tex2D(colorMapL, coords.xy), w.r, color); + color = mad(tex2D(colorMapL, coords.zw), w.g, color); + + coords = mad(float4(-a.b, 0.0, a.a, 0.0), PIXEL_SIZE.xxxx, texcoord.xyxy); + color = mad(tex2D(colorMapL, coords.xy), w.b, color); + color = mad(tex2D(colorMapL, coords.zw), w.a, color); + #else + float4 C = tex2D(colorMap, texcoord); + float4 Cleft = tex2D(colorMap, offset[0].xy); + float4 Ctop = tex2D(colorMap, offset[0].zw); + float4 Cright = tex2D(colorMap, offset[1].xy); + float4 Cbottom = tex2D(colorMap, offset[1].zw); + color = mad(lerp(C, Ctop, a.r), w.r, color); + color = mad(lerp(C, Cbottom, a.g), w.g, color); + color = mad(lerp(C, Cleft, a.b), w.b, color); + color = mad(lerp(C, Cright, a.a), w.a, color); + #endif + + // Normalize the resulting color and we are finished! + return color / sum; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/offsetV.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/offsetV.hlsl new file mode 100644 index 000000000..d9c922afd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/offsetV.hlsl @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform float2 texSize0; + +void main( inout float4 position : POSITION0, + inout float2 texcoord : TEXCOORD0, + out float4 offset[2] : TEXCOORD1 ) +{ + float2 PIXEL_SIZE = 1.0 / texSize0; + + texcoord.xy += PIXEL_SIZE * 0.5; + + offset[0] = texcoord.xyxy + PIXEL_SIZE.xyxy * float4(-1.0, 0.0, 0.0, -1.0); + offset[1] = texcoord.xyxy + PIXEL_SIZE.xyxy * float4( 1.0, 0.0, 0.0, 1.0); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/passthruV.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/passthruV.hlsl new file mode 100644 index 000000000..24ef534fd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/mlaa/passthruV.hlsl @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// An implementation of "Practical Morphological Anti-Aliasing" from +// GPU Pro 2 by Jorge Jimenez, Belen Masia, Jose I. Echevarria, +// Fernando Navarro, and Diego Gutierrez. +// +// http://www.iryoku.com/mlaa/ + + +uniform float2 texSize0; + +void main( inout float4 position : POSITION0, + inout float2 texcoord : TEXCOORD0) +{ + float2 PIXEL_SIZE = 1.0 / texSize0; + texcoord.xy += PIXEL_SIZE * 0.5; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/motionBlurP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/motionBlurP.hlsl new file mode 100644 index 000000000..8bc65fbc6 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/motionBlurP.hlsl @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" +#include "../torque.hlsl" +#include "../shaderModelAutoGen.hlsl" + +uniform float4x4 matPrevScreenToWorld; +uniform float4x4 matWorldToScreen; + +// Passed in from setShaderConsts() +uniform float velocityMultiplier; +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 1); + +float4 main(PFXVertToPix IN) : TORQUE_TARGET0 +{ + float samples = 5; + + // First get the prepass texture for uv channel 0 + float4 prepass = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ); + + // Next extract the depth + float depth = prepass.a; + + // Create the screen position + float4 screenPos = float4(IN.uv0.x*2-1, IN.uv0.y*2-1, depth*2-1, 1); + + // Calculate the world position + float4 D = mul(screenPos, matWorldToScreen); + float4 worldPos = D / D.w; + + // Now calculate the previous screen position + float4 previousPos = mul( worldPos, matPrevScreenToWorld ); + previousPos /= previousPos.w; + + // Calculate the XY velocity + float2 velocity = ((screenPos - previousPos) / velocityMultiplier).xy; + + // Generate the motion blur + float4 color = TORQUE_TEX2D(backBuffer, IN.uv0); + IN.uv0 += velocity; + + for(int i = 1; i blurNormalTol ) + { + usedCount++; + total += weight; + occlusion += TORQUE_TEX2D( occludeMap, uv ).r * weight; + } + } +} + +float4 main( VertToPix IN ) : TORQUE_TARGET0 +{ + //float4 centerTap; + float4 centerTap = TORQUE_PREPASS_UNCONDITION( prepassMap, IN.uv0.zw ); + + //return centerTap; + + //float centerOcclude = TORQUE_TEX2D( occludeMap, IN.uv0.zw ).r; + //return float4( centerOcclude.rrr, 1 ); + + float4 kernel = float4( 0.175, 0.275, 0.375, 0.475 ); //25f; + + float occlusion = 0; + int usedCount = 0; + float total = 0.0; + + sample( IN.uv0.xy, kernel.x, centerTap, usedCount, occlusion, total ); + sample( IN.uv1, kernel.y, centerTap, usedCount, occlusion, total ); + sample( IN.uv2, kernel.z, centerTap, usedCount, occlusion, total ); + sample( IN.uv3, kernel.w, centerTap, usedCount, occlusion, total ); + + sample( IN.uv4, kernel.x, centerTap, usedCount, occlusion, total ); + sample( IN.uv5, kernel.y, centerTap, usedCount, occlusion, total ); + sample( IN.uv6, kernel.z, centerTap, usedCount, occlusion, total ); + sample( IN.uv7, kernel.w, centerTap, usedCount, occlusion, total ); + + occlusion += TORQUE_TEX2D( occludeMap, IN.uv0.zw ).r * 0.5; + total += 0.5; + //occlusion /= 3.0; + + //occlusion /= (float)usedCount / 8.0; + occlusion /= total; + + return float4( occlusion.rrr, 1 ); + + + //return float4( 0,0,0,occlusion ); + + //float3 color = TORQUE_TEX2D( colorMap, IN.uv0.zw ); + + //return float4( color, occlusion ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_Blur_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_Blur_V.hlsl new file mode 100644 index 000000000..6ab278900 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_Blur_V.hlsl @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + + +uniform float2 texSize0; +uniform float2 oneOverTargetSize; +uniform float4 rtParams0; + +struct VertToPix +{ + float4 hpos : TORQUE_POSITION; + + float4 uv0 : TEXCOORD0; + float2 uv1 : TEXCOORD1; + float2 uv2 : TEXCOORD2; + float2 uv3 : TEXCOORD3; + + float2 uv4 : TEXCOORD4; + float2 uv5 : TEXCOORD5; + float2 uv6 : TEXCOORD6; + float2 uv7 : TEXCOORD7; +}; + +VertToPix main( PFXVert IN ) +{ + VertToPix OUT; + + OUT.hpos = float4(IN.pos,1.0); + + IN.uv = viewportCoordToRenderTarget( IN.uv, rtParams0 ); + + //float4 step = float4( 3.5, 2.5, 1.5, 0.5 ); + //float4 step = float4( 4.0, 3.0, 2.0, 1.0 ); + float4 step = float4( 9.0, 5.0, 2.5, 0.5 ); + + // I don't know why this offset is necessary, but it is. + //IN.uv = IN.uv * oneOverTargetSize; + + OUT.uv0.xy = IN.uv + ( ( BLUR_DIR * step.x ) / texSize0 ); + OUT.uv1 = IN.uv + ( ( BLUR_DIR * step.y ) / texSize0 ); + OUT.uv2 = IN.uv + ( ( BLUR_DIR * step.z ) / texSize0 ); + OUT.uv3 = IN.uv + ( ( BLUR_DIR * step.w ) / texSize0 ); + + OUT.uv4 = IN.uv - ( ( BLUR_DIR * step.x ) / texSize0 ); + OUT.uv5 = IN.uv - ( ( BLUR_DIR * step.y ) / texSize0 ); + OUT.uv6 = IN.uv - ( ( BLUR_DIR * step.z ) / texSize0 ); + OUT.uv7 = IN.uv - ( ( BLUR_DIR * step.w ) / texSize0 ); + + OUT.uv0.zw = IN.uv; + + /* + OUT.uv0 = viewportCoordToRenderTarget( OUT.uv0, rtParams0 ); + OUT.uv1 = viewportCoordToRenderTarget( OUT.uv1, rtParams0 ); + OUT.uv2 = viewportCoordToRenderTarget( OUT.uv2, rtParams0 ); + OUT.uv3 = viewportCoordToRenderTarget( OUT.uv3, rtParams0 ); + + OUT.uv4 = viewportCoordToRenderTarget( OUT.uv4, rtParams0 ); + OUT.uv5 = viewportCoordToRenderTarget( OUT.uv5, rtParams0 ); + OUT.uv6 = viewportCoordToRenderTarget( OUT.uv6, rtParams0 ); + OUT.uv7 = viewportCoordToRenderTarget( OUT.uv7, rtParams0 ); + */ + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_P.hlsl new file mode 100644 index 000000000..cb3ee2fe4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_P.hlsl @@ -0,0 +1,272 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../ShaderModelAutoGen.hlsl" +#include "./../postFx.hlsl" + +#define DOSMALL +#define DOLARGE + +TORQUE_UNIFORM_SAMPLER2D(prepassMap,0); +TORQUE_UNIFORM_SAMPLER2D(randNormalTex,1); +TORQUE_UNIFORM_SAMPLER1D(powTable,2); + +uniform float2 nearFar; +uniform float2 worldToScreenScale; +uniform float2 texSize0; +uniform float2 texSize1; +uniform float2 targetSize; + +// Script-set constants. + +uniform float overallStrength; + +uniform float sRadius; +uniform float sStrength; +uniform float sDepthMin; +uniform float sDepthMax; +uniform float sDepthPow; +uniform float sNormalTol; +uniform float sNormalPow; + +uniform float lRadius; +uniform float lStrength; +uniform float lDepthMin; +uniform float lDepthMax; +uniform float lDepthPow; +uniform float lNormalTol; +uniform float lNormalPow; + + +#ifndef QUALITY + #define QUALITY 2 +#endif + + +#if QUALITY == 0 + #define sSampleCount 4 + #define totalSampleCount 12 +#elif QUALITY == 1 + #define sSampleCount 6 + #define totalSampleCount 24 +#elif QUALITY == 2 + #define sSampleCount 8 + #define totalSampleCount 32 +#endif + + +float getOcclusion( float depthDiff, float depthMin, float depthMax, float depthPow, + float normalDiff, float dt, float normalTol, float normalPow ) +{ + if ( depthDiff < 0.0 ) + return 0.0; + + float delta = abs( depthDiff ); + + if ( delta < depthMin || delta > depthMax ) + return 0.0; + + delta = saturate( delta / depthMax ); + + if ( dt > 0.0 ) + normalDiff *= dt; + else + normalDiff = 1.0; + + + normalDiff *= 1.0 - ( dt * 0.5 + 0.5 ); + + return ( 1.0 - TORQUE_TEX1D( powTable, delta ).r ) * normalDiff; +} + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float3 ptSphere[32] = + { + float3( 0.295184, 0.077723, 0.068429 ), + float3( -0.271976, -0.365221, -0.838363 ), + float3( 0.547713, 0.467576, 0.488515 ), + float3( 0.662808, -0.031733, -0.584758 ), + float3( -0.025717, 0.218955, -0.657094 ), + float3( -0.310153, -0.365223, -0.370701 ), + float3( -0.101407, -0.006313, -0.747665 ), + float3( -0.769138, 0.360399, -0.086847 ), + float3( -0.271988, -0.275140, -0.905353 ), + float3( 0.096740, -0.566901, 0.700151 ), + float3( 0.562872, -0.735136, -0.094647 ), + float3( 0.379877, 0.359278, 0.190061 ), + float3( 0.519064, -0.023055, 0.405068 ), + float3( -0.301036, 0.114696, -0.088885 ), + float3( -0.282922, 0.598305, 0.487214 ), + float3( -0.181859, 0.251670, -0.679702 ), + float3( -0.191463, -0.635818, -0.512919 ), + float3( -0.293655, 0.427423, 0.078921 ), + float3( -0.267983, 0.680534, -0.132880 ), + float3( 0.139611, 0.319637, 0.477439 ), + float3( -0.352086, 0.311040, 0.653913 ), + float3( 0.321032, 0.805279, 0.487345 ), + float3( 0.073516, 0.820734, -0.414183 ), + float3( -0.155324, 0.589983, -0.411460 ), + float3( 0.335976, 0.170782, -0.527627 ), + float3( 0.463460, -0.355658, -0.167689 ), + float3( 0.222654, 0.596550, -0.769406 ), + float3( 0.922138, -0.042070, 0.147555 ), + float3( -0.727050, -0.329192, 0.369826 ), + float3( -0.090731, 0.533820, 0.463767 ), + float3( -0.323457, -0.876559, -0.238524 ), + float3( -0.663277, -0.372384, -0.342856 ) + }; + + // Sample a random normal for reflecting the + // sphere vector later in our loop. + float4 noiseMapUV = float4( ( IN.uv1 * ( targetSize / texSize1 ) ).xy, 0, 0 ); + float3 reflectNormal = normalize( TORQUE_TEX2DLOD( randNormalTex, noiseMapUV ).xyz * 2.0 - 1.0 ); + //return float4( reflectNormal, 1 ); + + float4 prepass = TORQUE_PREPASS_UNCONDITION( prepassMap, IN.uv0 ); + float3 normal = prepass.xyz; + float depth = prepass.a; + //return float4( ( depth ).xxx, 1 ); + + // Early out if too far away. + if ( depth > 0.99999999 ) + return float4( 0,0,0,0 ); + + // current fragment coords in screen space + float3 ep = float3( IN.uv0, depth ); + + float bl; + float3 baseRay, ray, se, occNorm, projRadius; + float normalDiff = 0; + float depthMin, depthMax, dt, depthDiff; + float4 occluderFragment; + int i; + float sOcclusion = 0.0; + float lOcclusion = 0.0; + + //------------------------------------------------------------ + // Small radius + //------------------------------------------------------------ + +#ifdef DOSMALL + + bl = 0.0; + + projRadius.xy = ( float2( sRadius.rr ) / ( depth * nearFar.y ) ) * ( worldToScreenScale / texSize0 ); + projRadius.z = sRadius / nearFar.y; + + depthMin = projRadius.z * sDepthMin; + depthMax = projRadius.z * sDepthMax; + + //float maxr = 1; + //radiusDepth = clamp( radiusDepth, 0.0001, maxr.rrr ); + //if ( radiusDepth.x < 1.0 / targetSize.x ) + // return color; + //radiusDepth.xyz = 0.0009; + + for ( i = 0; i < sSampleCount; i++ ) + { + baseRay = reflect( ptSphere[i], reflectNormal ); + + dt = dot( baseRay.xyz, normal ); + + baseRay *= sign( dt ); + + ray = ( projRadius * baseRay.xzy ); + ray.y = -ray.y; + + se = ep + ray; + + occluderFragment = TORQUE_PREPASS_UNCONDITION( prepassMap, se.xy ); + + depthDiff = se.z - occluderFragment.a; + + dt = dot( occluderFragment.xyz, baseRay.xyz ); + normalDiff = dot( occluderFragment.xyz, normal ); + + bl += getOcclusion( depthDiff, depthMin, depthMax, sDepthPow, normalDiff, dt, sNormalTol, sNormalPow ); + } + + sOcclusion = sStrength * ( bl / (float)sSampleCount ); + +#endif // DOSMALL + + + //------------------------------------------------------------ + // Large radius + //------------------------------------------------------------ + +#ifdef DOLARGE + + bl = 0.0; + + projRadius.xy = ( float2( lRadius.rr ) / ( depth * nearFar.y ) ) * ( worldToScreenScale / texSize0 ); + projRadius.z = lRadius / nearFar.y; + + depthMin = projRadius.z * lDepthMin; + depthMax = projRadius.z * lDepthMax; + + //projRadius.xy = clamp( projRadius.xy, 0.0, 0.01 ); + //float maxr = 1; + //radiusDepth = clamp( radiusDepth, 0.0001, maxr.rrr ); + //if ( radiusDepth.x < 1.0 / targetSize.x ) + // return color; + //radiusDepth.xyz = 0.0009; + + for ( i = sSampleCount; i < totalSampleCount; i++ ) + { + baseRay = reflect( ptSphere[i], reflectNormal ); + + dt = dot( baseRay.xyz, normal ); + + baseRay *= sign( dt ); + + ray = ( projRadius * baseRay.xzy ); + ray.y = -ray.y; + + se = ep + ray; + + occluderFragment = TORQUE_PREPASS_UNCONDITION( prepassMap, se.xy ); + + depthDiff = se.z - occluderFragment.a; + + normalDiff = dot( occluderFragment.xyz, normal ); + dt = dot( occluderFragment.xyz, baseRay.xyz ); + + bl += getOcclusion( depthDiff, depthMin, depthMax, lDepthPow, normalDiff, dt, lNormalTol, lNormalPow ); + } + + lOcclusion = lStrength * ( bl / (float)( totalSampleCount - sSampleCount ) ); + +#endif // DOLARGE + + float occlusion = saturate( max( sOcclusion, lOcclusion ) * overallStrength ); + + // Note black is unoccluded and white is fully occluded. This + // seems backwards, but it makes it simple to deal with the SSAO + // being disabled in the lighting shaders. + + return occlusion; +} + + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl new file mode 100644 index 000000000..696947d3e --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float power = pow( max( IN.uv0.x, 0 ), 0.1 ); + return float4( power, 0, 0, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl new file mode 100644 index 000000000..76f67e711 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./../postFx.hlsl" +#include "./../../torque.hlsl" + + +uniform float4 rtParams0; +uniform float4 rtParams1; +uniform float4 rtParams2; +uniform float4 rtParams3; + +PFXVertToPix main( PFXVert IN ) +{ + PFXVertToPix OUT; + + OUT.hpos = float4(IN.pos,1.0); + OUT.uv0 = IN.uv; //viewportCoordToRenderTarget( IN.uv, rtParams0 ); + OUT.uv1 = IN.uv; //viewportCoordToRenderTarget( IN.uv, rtParams1 ); + OUT.uv2 = IN.uv; //viewportCoordToRenderTarget( IN.uv, rtParams2 ); + OUT.uv3 = IN.uv; //viewportCoordToRenderTarget( IN.uv, rtParams3 ); + + OUT.wsEyeRay = IN.wsEyeRay; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl new file mode 100644 index 000000000..f0de9396f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +in vec4 uv0; +#define IN_uv0 uv0 +in vec2 uv1; +#define IN_uv1 uv1 +in vec2 uv2; +#define IN_uv2 uv2 +in vec2 uv3; +#define IN_uv3 uv3 + +in vec2 uv4; +#define IN_uv4 uv4 +in vec2 uv5; +#define IN_uv5 uv5 +in vec2 uv6; +#define IN_uv6 uv6 +in vec2 uv7; +#define IN_uv7 uv7 + +uniform sampler2D occludeMap ; +uniform sampler2D prepassMap ; +uniform float blurDepthTol; +uniform float blurNormalTol; + +out vec4 OUT_col; + +void _sample( vec2 uv, float weight, vec4 centerTap, inout int usedCount, inout float occlusion, inout float total ) +{ + //return; + vec4 tap = prepassUncondition( prepassMap, uv ); + + if ( abs( tap.a - centerTap.a ) < blurDepthTol ) + { + if ( dot( tap.xyz, centerTap.xyz ) > blurNormalTol ) + { + usedCount++; + total += weight; + occlusion += texture( occludeMap, uv ).r * weight; + } + } +} + +void main() +{ + //vec4 centerTap; + vec4 centerTap = prepassUncondition( prepassMap, IN_uv0.zw ); + + //return centerTap; + + //float centerOcclude = texture( occludeMap, IN_uv0.zw ).r; + //return vec4( centerOcclude.rrr, 1 ); + + vec4 kernel = vec4( 0.175, 0.275, 0.375, 0.475 ); //25f; + + float occlusion = 0; + int usedCount = 0; + float total = 0.0; + + _sample( IN_uv0.xy, kernel.x, centerTap, usedCount, occlusion, total ); + _sample( IN_uv1, kernel.y, centerTap, usedCount, occlusion, total ); + _sample( IN_uv2, kernel.z, centerTap, usedCount, occlusion, total ); + _sample( IN_uv3, kernel.w, centerTap, usedCount, occlusion, total ); + + _sample( IN_uv4, kernel.x, centerTap, usedCount, occlusion, total ); + _sample( IN_uv5, kernel.y, centerTap, usedCount, occlusion, total ); + _sample( IN_uv6, kernel.z, centerTap, usedCount, occlusion, total ); + _sample( IN_uv7, kernel.w, centerTap, usedCount, occlusion, total ); + + occlusion += texture( occludeMap, IN_uv0.zw ).r * 0.5; + total += 0.5; + //occlusion /= 3.0; + + //occlusion /= (float)usedCount / 8.0; + occlusion /= total; + + OUT_col = vec4( vec3(occlusion), 1 ); + + + //return vec4( 0,0,0,occlusion ); + + //vec3 color = texture( colorMap, IN_uv0.zw ); + + //return vec4( color, occlusion ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl new file mode 100644 index 000000000..45a52e890 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; +in vec2 vTexCoord0; + +#define IN_pos vPosition +#define _IN_uv vTexCoord0 + +uniform vec2 texSize0; +uniform vec4 rtParams0; +uniform vec2 oneOverTargetSize; + +#define OUT_hpos gl_Position + +out vec4 uv0; +#define OUT_uv0 uv0 +out vec2 uv1; +#define OUT_uv1 uv1 +out vec2 uv2; +#define OUT_uv2 uv2 +out vec2 uv3; +#define OUT_uv3 uv3 + +out vec2 uv4; +#define OUT_uv4 uv4 +out vec2 uv5; +#define OUT_uv5 uv5 +out vec2 uv6; +#define OUT_uv6 uv6 +out vec2 uv7; +#define OUT_uv7 uv7 + + +void main() +{ + OUT_hpos = IN_pos; + + vec2 IN_uv = viewportCoordToRenderTarget( _IN_uv, rtParams0 ); + + //vec4 step = vec4( 3.5, 2.5, 1.5, 0.5 ); + //vec4 step = vec4( 4.0, 3.0, 2.0, 1.0 ); + vec4 step = vec4( 9.0, 5.0, 2.5, 0.5 ); + + // I don't know why this offset is necessary, but it is. + //IN_uv = IN_uv * oneOverTargetSize; + + OUT_uv0.xy = IN_uv + ( ( BLUR_DIR * step.x ) / texSize0 ); + OUT_uv1 = IN_uv + ( ( BLUR_DIR * step.y ) / texSize0 ); + OUT_uv2 = IN_uv + ( ( BLUR_DIR * step.z ) / texSize0 ); + OUT_uv3 = IN_uv + ( ( BLUR_DIR * step.w ) / texSize0 ); + + OUT_uv4 = IN_uv - ( ( BLUR_DIR * step.x ) / texSize0 ); + OUT_uv5 = IN_uv - ( ( BLUR_DIR * step.y ) / texSize0 ); + OUT_uv6 = IN_uv - ( ( BLUR_DIR * step.z ) / texSize0 ); + OUT_uv7 = IN_uv - ( ( BLUR_DIR * step.w ) / texSize0 ); + + OUT_uv0.zw = IN_uv; + + /* + OUT_uv0 = viewportCoordToRenderTarget( OUT_uv0, rtParams0 ); + OUT_uv1 = viewportCoordToRenderTarget( OUT_uv1, rtParams0 ); + OUT_uv2 = viewportCoordToRenderTarget( OUT_uv2, rtParams0 ); + OUT_uv3 = viewportCoordToRenderTarget( OUT_uv3, rtParams0 ); + + OUT_uv4 = viewportCoordToRenderTarget( OUT_uv4, rtParams0 ); + OUT_uv5 = viewportCoordToRenderTarget( OUT_uv5, rtParams0 ); + OUT_uv6 = viewportCoordToRenderTarget( OUT_uv6, rtParams0 ); + OUT_uv7 = viewportCoordToRenderTarget( OUT_uv7, rtParams0 ); + */ + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_P.glsl new file mode 100644 index 000000000..7ee6a412a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_P.glsl @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/postFX.glsl" + +#define DOSMALL +#define DOLARGE + +uniform sampler2D prepassMap ; +uniform sampler2D randNormalTex ; +uniform sampler1D powTable ; + +uniform vec2 nearFar; +uniform vec2 worldToScreenScale; +uniform vec2 texSize0; +uniform vec2 texSize1; +uniform vec2 targetSize; + +// Script-set constants. + +uniform float overallStrength; + +uniform float sRadius; +uniform float sStrength; +uniform float sDepthMin; +uniform float sDepthMax; +uniform float sDepthPow; +uniform float sNormalTol; +uniform float sNormalPow; + +uniform float lRadius; +uniform float lStrength; +uniform float lDepthMin; +uniform float lDepthMax; +uniform float lDepthPow; +uniform float lNormalTol; +uniform float lNormalPow; + +out vec4 OUT_col; + + +#ifndef QUALITY + #define QUALITY 2 +#endif + + +#if QUALITY == 0 + #define sSampleCount 4 + #define totalSampleCount 12 +#elif QUALITY == 1 + #define sSampleCount 6 + #define totalSampleCount 24 +#elif QUALITY == 2 + #define sSampleCount 8 + #define totalSampleCount 32 +#endif + + +float getOcclusion( float depthDiff, float depthMin, float depthMax, float depthPow, + float normalDiff, float dt, float normalTol, float normalPow ) +{ + if ( depthDiff < 0.0 ) + return 0.0; + + float delta = abs( depthDiff ); + + if ( delta < depthMin || delta > depthMax ) + return 0.0; + + delta = saturate( delta / depthMax ); + + if ( dt > 0.0 ) + normalDiff *= dt; + else + normalDiff = 1.0; + + + normalDiff *= 1.0 - ( dt * 0.5 + 0.5 ); + + return ( 1.0 - texture( powTable, delta ).r ) * normalDiff; +} + + +void main() +{ + const vec3 ptSphere[32] = vec3[] + ( + vec3( 0.295184, 0.077723, 0.068429 ), + vec3( -0.271976, -0.365221, -0.838363 ), + vec3( 0.547713, 0.467576, 0.488515 ), + vec3( 0.662808, -0.031733, -0.584758 ), + vec3( -0.025717, 0.218955, -0.657094 ), + vec3( -0.310153, -0.365223, -0.370701 ), + vec3( -0.101407, -0.006313, -0.747665 ), + vec3( -0.769138, 0.360399, -0.086847 ), + vec3( -0.271988, -0.275140, -0.905353 ), + vec3( 0.096740, -0.566901, 0.700151 ), + vec3( 0.562872, -0.735136, -0.094647 ), + vec3( 0.379877, 0.359278, 0.190061 ), + vec3( 0.519064, -0.023055, 0.405068 ), + vec3( -0.301036, 0.114696, -0.088885 ), + vec3( -0.282922, 0.598305, 0.487214 ), + vec3( -0.181859, 0.251670, -0.679702 ), + vec3( -0.191463, -0.635818, -0.512919 ), + vec3( -0.293655, 0.427423, 0.078921 ), + vec3( -0.267983, 0.680534, -0.132880 ), + vec3( 0.139611, 0.319637, 0.477439 ), + vec3( -0.352086, 0.311040, 0.653913 ), + vec3( 0.321032, 0.805279, 0.487345 ), + vec3( 0.073516, 0.820734, -0.414183 ), + vec3( -0.155324, 0.589983, -0.411460 ), + vec3( 0.335976, 0.170782, -0.527627 ), + vec3( 0.463460, -0.355658, -0.167689 ), + vec3( 0.222654, 0.596550, -0.769406 ), + vec3( 0.922138, -0.042070, 0.147555 ), + vec3( -0.727050, -0.329192, 0.369826 ), + vec3( -0.090731, 0.533820, 0.463767 ), + vec3( -0.323457, -0.876559, -0.238524 ), + vec3( -0.663277, -0.372384, -0.342856 ) + ); + + // Sample a random normal for reflecting the + // sphere vector later in our loop. + vec4 noiseMapUV = vec4( ( IN_uv1 * ( targetSize / texSize1 ) ).xy, 0, 0 ); + vec3 reflectNormal = normalize( tex2Dlod( randNormalTex, noiseMapUV ).xyz * 2.0 - 1.0 ); + //return vec4( reflectNormal, 1 ); + + vec4 prepass = prepassUncondition( prepassMap, IN_uv0 ); + vec3 normal = prepass.xyz; + float depth = prepass.a; + //return vec4( ( depth ).xxx, 1 ); + + // Early out if too far away. + if ( depth > 0.99999999 ) + { + OUT_col = vec4( 0,0,0,0 ); + return; + } + + // current fragment coords in screen space + vec3 ep = vec3( IN_uv0, depth ); + + float bl; + vec3 baseRay, ray, se, occNorm, projRadius; + float normalDiff = 0; + float depthMin, depthMax, dt, depthDiff; + vec4 occluderFragment; + int i; + float sOcclusion = 0.0; + float lOcclusion = 0.0; + + //------------------------------------------------------------ + // Small radius + //------------------------------------------------------------ + +#ifdef DOSMALL + + bl = 0.0; + + projRadius.xy = ( vec2( sRadius ) / ( depth * nearFar.y ) ) * ( worldToScreenScale / texSize0 ); + projRadius.z = sRadius / nearFar.y; + + depthMin = projRadius.z * sDepthMin; + depthMax = projRadius.z * sDepthMax; + + //float maxr = 1; + //radiusDepth = clamp( radiusDepth, 0.0001, maxr.rrr ); + //if ( radiusDepth.x < 1.0 / targetSize.x ) + // return color; + //radiusDepth.xyz = 0.0009; + + for ( i = 0; i < sSampleCount; i++ ) + { + baseRay = reflect( ptSphere[i], reflectNormal ); + + dt = dot( baseRay.xyz, normal ); + + baseRay *= sign( dt ); + + ray = ( projRadius * baseRay.xzy ); + ray.y = -ray.y; + + se = ep + ray; + + occluderFragment = prepassUncondition( prepassMap, se.xy ); + + depthDiff = se.z - occluderFragment.a; + + dt = dot( occluderFragment.xyz, baseRay.xyz ); + normalDiff = dot( occluderFragment.xyz, normal ); + + bl += getOcclusion( depthDiff, depthMin, depthMax, sDepthPow, normalDiff, dt, sNormalTol, sNormalPow ); + } + + sOcclusion = sStrength * ( bl / float(sSampleCount) ); + +#endif // DOSMALL + + + //------------------------------------------------------------ + // Large radius + //------------------------------------------------------------ + +#ifdef DOLARGE + + bl = 0.0; + + projRadius.xy = ( vec2( lRadius ) / ( depth * nearFar.y ) ) * ( worldToScreenScale / texSize0 ); + projRadius.z = lRadius / nearFar.y; + + depthMin = projRadius.z * lDepthMin; + depthMax = projRadius.z * lDepthMax; + + //projRadius.xy = clamp( projRadius.xy, 0.0, 0.01 ); + //float maxr = 1; + //radiusDepth = clamp( radiusDepth, 0.0001, maxr.rrr ); + //if ( radiusDepth.x < 1.0 / targetSize.x ) + // return color; + //radiusDepth.xyz = 0.0009; + + for ( i = sSampleCount; i < totalSampleCount; i++ ) + { + baseRay = reflect( ptSphere[i], reflectNormal ); + + dt = dot( baseRay.xyz, normal ); + + baseRay *= sign( dt ); + + ray = ( projRadius * baseRay.xzy ); + ray.y = -ray.y; + + se = ep + ray; + + occluderFragment = prepassUncondition( prepassMap, se.xy ); + + depthDiff = se.z - occluderFragment.a; + + normalDiff = dot( occluderFragment.xyz, normal ); + dt = dot( occluderFragment.xyz, baseRay.xyz ); + + bl += getOcclusion( depthDiff, depthMin, depthMax, lDepthPow, normalDiff, dt, lNormalTol, lNormalPow ); + } + + lOcclusion = lStrength * ( bl / float( totalSampleCount - sSampleCount ) ); + +#endif // DOLARGE + + float occlusion = saturate( max( sOcclusion, lOcclusion ) * overallStrength ); + + // Note black is unoccluded and white is fully occluded. This + // seems backwards, but it makes it simple to deal with the SSAO + // being disabled in the lighting shaders. + + OUT_col = vec4(occlusion, vec3(0.0)); +} + + diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl new file mode 100644 index 000000000..4f49479ba --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" + +in vec2 uv0; +#define IN_uv0 uv0 + +out vec4 OUT_col; + +void main() +{ + float power = pow( max( IN_uv0.x, 0 ), 0.1 ); + OUT_col = vec4( power, 0, 0, 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl new file mode 100644 index 000000000..a193f63ce --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/torque.glsl" +#include "../../../gl/hlslCompat.glsl" +#include "../../gl/postFX.glsl" + +void main() +{ + OUT_hpos = IN_pos; + OUT_uv0 = IN_uv; //viewportCoordToRenderTarget( IN_uv, rtParams0 ); + OUT_uv1 = IN_uv; //viewportCoordToRenderTarget( IN_uv, rtParams1 ); + OUT_uv2 = IN_uv; //viewportCoordToRenderTarget( IN_uv, rtParams2 ); + OUT_uv3 = IN_uv; //viewportCoordToRenderTarget( IN_uv, rtParams3 ); + + OUT_wsEyeRay = IN_wsEyeRay; + + correctSSP(gl_Position); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/turbulenceP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/turbulenceP.hlsl new file mode 100644 index 000000000..c8c572ae7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/turbulenceP.hlsl @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" + +uniform float accumTime; +uniform float2 projectionOffset; +uniform float4 targetViewport; +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + float speed = 2.0; + float distortion = 6.0; + + float y = IN.uv0.y + (cos((IN.uv0.y+projectionOffset.y) * distortion + accumTime * speed) * 0.01); + float x = IN.uv0.x + (sin((IN.uv0.x+projectionOffset.x) * distortion + accumTime * speed) * 0.01); + + // Clamp the calculated uv values to be within the target's viewport + y = clamp(y, targetViewport.y, targetViewport.w); + x = clamp(x, targetViewport.x, targetViewport.z); + + return TORQUE_TEX2D(inputTex, float2(x, y)); +} diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/underwaterFogP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/underwaterFogP.hlsl new file mode 100644 index 000000000..80a8c4c77 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/underwaterFogP.hlsl @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "./postFx.hlsl" +#include "../torque.hlsl" +#include "../shaderModelAutoGen.hlsl" +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +// oceanFogData +#define FOG_DENSITY waterFogData[0] +#define FOG_DENSITY_OFFSET waterFogData[1] +#define WET_DEPTH waterFogData[2] +#define WET_DARKENING waterFogData[3] + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- + +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 0); +TORQUE_UNIFORM_SAMPLER2D(backbuffer, 1); +TORQUE_UNIFORM_SAMPLER1D(waterDepthGradMap, 2); + +uniform float3 eyePosWorld; +uniform float waterDepthGradMax; +uniform float3 ambientColor; +uniform float4 waterColor; +uniform float4 waterFogData; +uniform float4 waterFogPlane; +uniform float2 nearFar; +uniform float4 rtParams0; + + +float4 main( PFXVertToPix IN ) : TORQUE_TARGET0 +{ + //float2 prepassCoord = IN.uv0; + //IN.uv0 = ( IN.uv0.xy * rtParams0.zw ) + rtParams0.xy; + float depth = TORQUE_PREPASS_UNCONDITION( prepassTex, IN.uv0 ).w; + //return float4( depth.rrr, 1 ); + + // Skip fogging the extreme far plane so that + // the canvas clear color always appears. + //clip( 0.9 - depth ); + + // We assume that the eye position is below water because + // otherwise this shader/posteffect should not be active. + + depth *= nearFar.y; + + float3 eyeRay = normalize( IN.wsEyeRay ); + + float3 rayStart = eyePosWorld; + float3 rayEnd = eyePosWorld + ( eyeRay * depth ); + //return float4( rayEnd, 1 ); + + float4 plane = waterFogPlane; //float4( 0, 0, 1, -waterHeight ); + //plane.w -= 0.15; + + float startSide = dot( plane.xyz, rayStart ) + plane.w; + if ( startSide > 0 ) + { + rayStart.z -= ( startSide ); + //return float4( 1, 0, 0, 1 ); + } + + float3 hitPos; + float3 ray = rayEnd - rayStart; + float rayLen = length( ray ); + float3 rayDir = normalize( ray ); + + float endSide = dot( plane.xyz, rayEnd ) + plane.w; + float planeDist; + + if ( endSide < -0.005 ) + { + //return float4( 0, 0, 1, 1 ); + hitPos = rayEnd; + planeDist = endSide; + } + else + { + //return float4( 0, 0, 0, 0 ); + float den = dot( ray, plane.xyz ); + + // Parallal to the plane, return the endPnt. + //if ( den == 0.0f ) + // return endPnt; + + float dist = -( dot( plane.xyz, rayStart ) + plane.w ) / den; + if ( dist < 0.0 ) + dist = 0.0; + //return float4( 1, 0, 0, 1 ); + //return float4( ( dist ).rrr, 1 ); + + + hitPos = lerp( rayStart, rayEnd, dist ); + + planeDist = dist; + } + + float delta = length( hitPos - rayStart ); + + float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * ( delta - FOG_DENSITY_OFFSET ) ) ); + //return float4( fogAmt.rrr, 1 ); + + // Calculate the water "base" color based on depth. + float4 fogColor = waterColor * TORQUE_TEX1D( waterDepthGradMap, saturate( delta / waterDepthGradMax ) ); + // Modulate baseColor by the ambientColor. + fogColor *= float4( ambientColor.rgb, 1 ); + + float3 inColor = hdrDecode( TORQUE_TEX2D( backbuffer, IN.uv0 ).rgb ); + inColor.rgb *= 1.0 - saturate( abs( planeDist ) / WET_DEPTH ) * WET_DARKENING; + //return float4( inColor, 1 ); + + float3 outColor = lerp( inColor, fogColor.rgb, fogAmt ); + + return float4( hdrEncode( outColor ), 1 ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/vignette/VignetteP.hlsl b/Templates/BaseGame/game/data/shaders/common/postFx/vignette/VignetteP.hlsl new file mode 100644 index 000000000..c518a2145 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/vignette/VignetteP.hlsl @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" + +TORQUE_UNIFORM_SAMPLER2D(backBuffer, 0); +uniform float Vmax; +uniform float Vmin; + +float4 main(PFXVertToPix IN) : TORQUE_TARGET0 +{ + float4 base = TORQUE_TEX2D(backBuffer, IN.uv0); + float dist = distance(IN.uv0, float2(0.5,0.5)); + base.rgb *= smoothstep(Vmax, Vmin, dist); + return base; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/postFx/vignette/gl/VignetteP.glsl b/Templates/BaseGame/game/data/shaders/common/postFx/vignette/gl/VignetteP.glsl new file mode 100644 index 000000000..35de95c34 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/postFx/vignette/gl/VignetteP.glsl @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D backBuffer; +uniform float Vmax; +uniform float Vmin; + +in vec2 uv0; +#define IN_uv0 uv0 + +out vec4 OUT_col; + +void main() +{ + vec4 base = texture(backBuffer, IN_uv0); + float dist = distance(IN_uv0, vec2(0.5,0.5)); + base.rgb *= smoothstep(Vmax, Vmin, dist); + OUT_col = base; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/precipP.hlsl b/Templates/BaseGame/game/data/shaders/common/precipP.hlsl new file mode 100644 index 000000000..069ba4992 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/precipP.hlsl @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Conn +{ + float4 position : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +}; + +struct Frag +{ + float4 col : TORQUE_TARGET0; +}; + +TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Frag main( Conn In) +{ + Frag Out; + + Out.col = TORQUE_TEX2D(diffuseMap, In.texCoord) * In.color; + + return Out; +} diff --git a/Templates/BaseGame/game/data/shaders/common/precipV.hlsl b/Templates/BaseGame/game/data/shaders/common/precipV.hlsl new file mode 100644 index 000000000..3c40942c7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/precipV.hlsl @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//***************************************************************************** +// Precipitation vertex shader +//***************************************************************************** +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Vert +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +}; + +struct Conn +{ + float4 position : TORQUE_POSITION; + float2 texCoord : TEXCOORD0; + float4 color : COLOR0; +}; + +uniform float4x4 modelview : register(C0); +uniform float2 fadeStartEnd : register(C4); +uniform float3 cameraPos : register(C5); +uniform float3 ambient : register(C6); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Conn main( Vert In ) +{ + Conn Out; + + Out.position = mul(modelview, float4(In.position,1.0)); + Out.texCoord = In.texCoord; + Out.color = float4( ambient.r, ambient.g, ambient.b, 1 ); + + // Do we need to do a distance fade? + if ( fadeStartEnd.x < fadeStartEnd.y ) { + + float distance = length( cameraPos - In.position ); + Out.color.a = abs( clamp( ( distance - fadeStartEnd.x ) / ( fadeStartEnd.y - fadeStartEnd.x ), 0, 1 ) - 1 ); + } + + return Out; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/projectedShadowP.hlsl b/Templates/BaseGame/game/data/shaders/common/projectedShadowP.hlsl new file mode 100644 index 000000000..88713bd52 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/projectedShadowP.hlsl @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Conn +{ + float4 position : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float fade : TEXCOORD1; +}; + +TORQUE_UNIFORM_SAMPLER2D(inputTex, 0); +uniform float4 ambient; + +float4 main( Conn IN ) : TORQUE_TARGET0 +{ + float shadow = TORQUE_TEX2D( inputTex, IN.texCoord ).a * IN.color.a; + return ( ambient * shadow ) + ( 1 - shadow ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/projectedShadowV.hlsl b/Templates/BaseGame/game/data/shaders/common/projectedShadowV.hlsl new file mode 100644 index 000000000..38b1bd3e2 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/projectedShadowV.hlsl @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +struct Vert +{ + float3 position : POSITION; + float3 normal : NORMAL; + float3 T : TANGENT; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; +}; + +struct Conn +{ + float4 position : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float fade : TEXCOORD1; +}; + +uniform float4x4 modelview; +uniform float shadowLength; +uniform float3 shadowCasterPosition; + +Conn main( Vert In ) +{ + Conn Out; + + // Decals are in world space. + Out.position = mul( modelview, float4( In.position, 1.0 ) ); + + Out.color = In.color; + Out.texCoord = In.texCoord; + + float fromCasterDist = length( In.position - shadowCasterPosition ) - shadowLength; + Out.fade = 1.0 - saturate( fromCasterDist / shadowLength ); + + return Out; +} diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderP.hlsl new file mode 100644 index 000000000..f4c6c7b04 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -0,0 +1,19 @@ +#define IN_HLSL +#include "../shdrConsts.h" +#include "../shaderModel.hlsl" + +struct v2f +{ + float4 hpos : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +float4 main(v2f IN) : TORQUE_TARGET0 +{ + float fade = 1.0 - abs(IN.shiftdata.y - 0.5) * 2.0; + IN.color.xyz = IN.color.xyz + pow(fade, 4) / 10; + IN.color.a = IN.color.a * fade; + return IN.color; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderV.hlsl new file mode 100644 index 000000000..1a6bc85e4 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -0,0 +1,35 @@ +#define IN_HLSL +#include "../shdrConsts.h" +#include "../shaderModel.hlsl" + +struct a2v +{ + float3 position : POSITION; + float3 normal : NORMAL; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +struct v2f +{ + float4 hpos : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, float4(IN.position, 1.0)); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderP.glsl b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderP.glsl new file mode 100644 index 000000000..f3f83ce30 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderP.glsl @@ -0,0 +1,20 @@ +#include "../../gl/hlslCompat.glsl" + +in float4 _hpos; +in float2 _texCoord; +in float2 _shiftdata; +in float4 _color; + +#define IN_hpos _hpos +#define IN_texCoord _texCoord +#define IN_shiftdata _shiftdata +#define IN_color _color + +out float4 OUT_col; + +void main() +{ + float fade = 1.0 - abs(IN_shiftdata.y - 0.5) * 2.0; + OUT_col.xyz = IN_color.xyz + pow(fade, 4) / 10; + OUT_col.a = IN_color.a * fade; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderV.glsl b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderV.glsl new file mode 100644 index 000000000..9f6826d55 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/basicRibbonShaderV.glsl @@ -0,0 +1,37 @@ +#include "../../gl/hlslCompat.glsl" + +in float2 vTexCoord0; +in float2 vTexCoord1; +in float3 vNormal; +in float4 vPosition; +in float4 vColor; + +#define IN_texCoord vTexCoord0 +#define IN_shiftdata vTexCoord1 +#define IN_normal vNormal +#define IN_position vPosition +#define IN_color vColor + +out float4 _hpos; +out float2 _texCoord; +out float2 _shiftdata; +out float4 _color; + +#define OUT_hpos _hpos +#define OUT_texCoord _texCoord +#define OUT_shiftdata _shiftdata +#define OUT_color _color + +uniform float4x4 modelview; +uniform float3 eyePos; + +void main() +{ + OUT_hpos = tMul(modelview, IN_position); + OUT_color = IN_color; + OUT_texCoord = IN_texCoord; + OUT_shiftdata = IN_shiftdata; + + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderP.glsl b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderP.glsl new file mode 100644 index 000000000..cd3e72d43 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderP.glsl @@ -0,0 +1,22 @@ +#include "../../gl/hlslCompat.glsl" +#include "../../gl/torque.glsl" + +in float4 _hpos; +in float2 _texCoord; +in float2 _shiftdata; +in float4 _color; + +#define IN_hpos _hpos +#define IN_texCoord _texCoord +#define IN_shiftdata _shiftdata +#define IN_color _color + +out float4 OUT_col; +uniform sampler2D ribTex; + +void main() +{ + float4 Tex = tex2D(ribTex,IN_texCoord); + Tex.a *= IN_color.a; + OUT_col = hdrEncode(Tex); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderV.glsl b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderV.glsl new file mode 100644 index 000000000..9f6826d55 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/gl/texRibbonShaderV.glsl @@ -0,0 +1,37 @@ +#include "../../gl/hlslCompat.glsl" + +in float2 vTexCoord0; +in float2 vTexCoord1; +in float3 vNormal; +in float4 vPosition; +in float4 vColor; + +#define IN_texCoord vTexCoord0 +#define IN_shiftdata vTexCoord1 +#define IN_normal vNormal +#define IN_position vPosition +#define IN_color vColor + +out float4 _hpos; +out float2 _texCoord; +out float2 _shiftdata; +out float4 _color; + +#define OUT_hpos _hpos +#define OUT_texCoord _texCoord +#define OUT_shiftdata _shiftdata +#define OUT_color _color + +uniform float4x4 modelview; +uniform float3 eyePos; + +void main() +{ + OUT_hpos = tMul(modelview, IN_position); + OUT_color = IN_color; + OUT_texCoord = IN_texCoord; + OUT_shiftdata = IN_shiftdata; + + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderP.hlsl b/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderP.hlsl new file mode 100644 index 000000000..55bae76fd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderP.hlsl @@ -0,0 +1,21 @@ +#define IN_HLSL +#include "../shdrConsts.h" +#include "../torque.hlsl" + + +TORQUE_UNIFORM_SAMPLER2D(ribTex, 0); + +struct v2f +{ + float4 hpos : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +float4 main(v2f IN) : TORQUE_TARGET0 +{ + float4 Tex = TORQUE_TEX2D(ribTex,IN.texCoord); + Tex.a *= IN.color.a; + return hdrEncode(Tex); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderV.hlsl b/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderV.hlsl new file mode 100644 index 000000000..2ce4f62fd --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/ribbons/texRibbonShaderV.hlsl @@ -0,0 +1,35 @@ +#define IN_HLSL +#include "../shdrConsts.h" +#include "../shaderModel.hlsl" + +struct a2v +{ + float3 position : POSITION; + float4 color : COLOR0; + float3 normal : NORMAL; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +struct v2f +{ + float4 hpos : TORQUE_POSITION; + float4 color : COLOR0; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, float4(IN.position,1.0)); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/scatterSkyP.hlsl b/Templates/BaseGame/game/data/shaders/common/scatterSkyP.hlsl new file mode 100644 index 000000000..84e0854e0 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/scatterSkyP.hlsl @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" +#include "torque.hlsl" + +struct Conn +{ + float4 hpos : TORQUE_POSITION; + float4 rayleighColor : TEXCOORD0; + float4 mieColor : TEXCOORD1; + float3 v3Direction : TEXCOORD2; + float3 pos : TEXCOORD3; +}; + +TORQUE_UNIFORM_SAMPLERCUBE(nightSky, 0); +uniform float4 nightColor; +uniform float2 nightInterpAndExposure; +uniform float useCubemap; +uniform float3 lightDir; +uniform float3 sunDir; + +float4 main( Conn In ) : TORQUE_TARGET0 +{ + + float fCos = dot( lightDir, In.v3Direction ) / length(In.v3Direction); + float fCos2 = fCos*fCos; + + float g = -0.991; + float g2 = -0.991 * -0.991; + + float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); + + float4 color = In.rayleighColor + fMiePhase * In.mieColor; + color.a = color.b; + + float4 Out; + + float4 nightSkyColor = TORQUE_TEXCUBE(nightSky, -In.v3Direction); + nightSkyColor = lerp( nightColor, nightSkyColor, useCubemap ); + + float fac = dot( normalize( In.pos ), sunDir ); + fac = max( nightInterpAndExposure.y, pow( saturate( fac ), 2 ) ); + Out = lerp( color, nightSkyColor, nightInterpAndExposure.y ); + + Out.a = 1; + Out = saturate(Out); + + return hdrEncode( Out ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/scatterSkyV.hlsl b/Templates/BaseGame/game/data/shaders/common/scatterSkyV.hlsl new file mode 100644 index 000000000..2052448db --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/scatterSkyV.hlsl @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "shaderModel.hlsl" + +// The scale equation calculated by Vernier's Graphical Analysis +float vernierScale(float fCos) +{ + float x = 1.0 - fCos; + float x5 = x * 5.25; + float x5p6 = (-6.80 + x5); + float xnew = (3.83 + x * x5p6); + float xfinal = (0.459 + x * xnew); + float xfinal2 = -0.00287 + x * xfinal; + float outx = exp( xfinal2 ); + return 0.25 * outx; +} + +// This is the shader input vertex structure. +struct Vert +{ + // .xyz = point + float3 position : POSITION; +}; + +// This is the shader output data. +struct Conn +{ + float4 position : TORQUE_POSITION; + float4 rayleighColor : TEXCOORD0; + float4 mieColor : TEXCOORD1; + float3 v3Direction : TEXCOORD2; + float3 pos : TEXCOORD3; +}; + +float3 desaturate(const float3 color, const float desaturation) +{ + const float3 gray_conv = float3 (0.30, 0.59, 0.11); + return lerp(color, dot(gray_conv , color), desaturation); +} + +uniform float4x4 modelView; +uniform float4 misc; +uniform float4 sphereRadii; +uniform float4 scatteringCoeffs; +uniform float4 colorize; +uniform float3 camPos; +uniform float3 lightDir; +uniform float4 invWaveLength; + +Conn main( Vert In ) +{ + // Pull some variables out: + float camHeight = misc.x; + float camHeightSqr = misc.y; + + float scale = misc.z; + float scaleOverScaleDepth = misc.w; + + float outerRadius = sphereRadii.x; + float outerRadiusSqr = sphereRadii.y; + + float innerRadius = sphereRadii.z; + float innerRadiusSqr = sphereRadii.w; + + float rayleighBrightness = scatteringCoeffs.x; // Kr * ESun + float rayleigh4PI = scatteringCoeffs.y; // Kr * 4 * PI + + float mieBrightness = scatteringCoeffs.z; // Km * ESun + float mie4PI = scatteringCoeffs.w; // Km * 4 * PI + + // Get the ray from the camera to the vertex, + // and its length (which is the far point of the ray + // passing through the atmosphere). + float4 v3Pos = float4(In.position,1.0) / 6378000.0; // outerRadius; + float3 newCamPos = float3( 0, 0, camHeight ); + v3Pos.z += innerRadius; + float3 v3Ray = v3Pos.xyz - newCamPos; + float fFar = length(v3Ray); + v3Ray /= fFar; + + // Calculate the ray's starting position, + // then calculate its scattering offset. + float3 v3Start = newCamPos; + float fHeight = length(v3Start); + float fDepth = exp(scaleOverScaleDepth * (innerRadius - camHeight)); + float fStartAngle = dot(v3Ray, v3Start) / fHeight; + + float fStartOffset = fDepth * vernierScale( fStartAngle ); + + // Initialize the scattering loop variables. + float fSampleLength = fFar / 2; + float fScaledLength = fSampleLength * scale; + float3 v3SampleRay = v3Ray * fSampleLength; + float3 v3SamplePoint = v3Start + v3SampleRay * 0.5; + + // Now loop through the sample rays + float3 v3FrontColor = float3(0.0, 0.0, 0.0); + for(int i=0; i<2; i++) + { + float fHeight = length(v3SamplePoint); + float fDepth = exp(scaleOverScaleDepth * (innerRadius - fHeight)); + float fLightAngle = dot(lightDir, v3SamplePoint) / fHeight; + float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; + + float vscale3 = vernierScale( fCameraAngle ); + float vscale2 = vernierScale( fLightAngle ); + + float fScatter = (fStartOffset + fDepth*(vscale2 - vscale3)); + float3 v3Attenuate = exp(-fScatter * (invWaveLength.xyz * rayleigh4PI + mie4PI)); + v3FrontColor += v3Attenuate * (fDepth * fScaledLength); + v3SamplePoint += v3SampleRay; + } + + Conn Out; + + // Finally, scale the Mie and Rayleigh colors + // and set up the varying variables for the pixel shader. + Out.position = mul( modelView, float4(In.position,1.0) ); + Out.mieColor.rgb = v3FrontColor * mieBrightness; + Out.mieColor.a = 1.0f; + Out.rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); + Out.rayleighColor.a = 1.0f; + Out.v3Direction = newCamPos - v3Pos.xyz; + Out.pos = In.position; + +#ifdef USE_COLORIZE + + Out.rayleighColor.rgb = desaturate(Out.rayleighColor.rgb, 1) * colorize.a; + + Out.rayleighColor.r *= colorize.r; + Out.rayleighColor.g *= colorize.g; + Out.rayleighColor.b *= colorize.b; + +#endif + + return Out; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/shaders/common/shaderModel.hlsl b/Templates/BaseGame/game/data/shaders/common/shaderModel.hlsl new file mode 100644 index 000000000..70ce3a02d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/shaderModel.hlsl @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2015 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_SHADERMODEL_ +#define _TORQUE_SHADERMODEL_ + +// Portability helpers for different shader models +//Shader model 1.0 - 3.0 +#if (TORQUE_SM >= 10 && TORQUE_SM <=30) + // Semantics + #define TORQUE_POSITION POSITION + #define TORQUE_DEPTH DEPTH + #define TORQUE_TARGET0 COLOR0 + #define TORQUE_TARGET1 COLOR1 + #define TORQUE_TARGET2 COLOR2 + #define TORQUE_TARGET3 COLOR3 + + // Sampler uniforms + #define TORQUE_UNIFORM_SAMPLER1D(tex,regist) uniform sampler1D tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLER2D(tex,regist) uniform sampler2D tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLER3D(tex,regist) uniform sampler3D tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLERCUBE(tex,regist) uniform samplerCUBE tex : register(S##regist) + // Sampling functions + #define TORQUE_TEX1D(tex,coords) tex1D(tex,coords) + #define TORQUE_TEX2D(tex,coords) tex2D(tex,coords) + #define TORQUE_TEX2DPROJ(tex,coords) tex2Dproj(tex,coords) //this really is sm 2 or later + #define TORQUE_TEX3D(tex,coords) tex3D(tex,coords) + #define TORQUE_TEXCUBE(tex,coords) texCUBE(tex,coords) + + //Shader model 3.0 only + #if TORQUE_SM == 30 + #define TORQUE_VPOS VPOS // This is a float2 + // The mipmap LOD is specified in coord.w + #define TORQUE_TEX2DLOD(tex,coords) tex2Dlod(tex,coords) + #endif + + //helper if you want to pass sampler/texture in a function + //2D + #define TORQUE_SAMPLER2D(tex) sampler2D tex + #define TORQUE_SAMPLER2D_MAKEARG(tex) tex + //Cube + #define TORQUE_SAMPLERCUBE(tex) samplerCUBE tex + #define TORQUE_SAMPLERCUBE_MAKEARG(tex) tex +// Shader model 4.0+ +#elif TORQUE_SM >= 40 + #define TORQUE_POSITION SV_Position + #define TORQUE_DEPTH SV_Depth + #define TORQUE_VPOS SV_Position //note float4 compared to SM 3 where it is a float2 + #define TORQUE_TARGET0 SV_Target0 + #define TORQUE_TARGET1 SV_Target1 + #define TORQUE_TARGET2 SV_Target2 + #define TORQUE_TARGET3 SV_Target3 + // Sampler uniforms + //1D is emulated to a 2D for now + #define TORQUE_UNIFORM_SAMPLER1D(tex,regist) uniform Texture2D texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLER2D(tex,regist) uniform Texture2D texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLER3D(tex,regist) uniform Texture3D texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) + #define TORQUE_UNIFORM_SAMPLERCUBE(tex,regist) uniform TextureCube texture_##tex : register(T##regist); uniform SamplerState tex : register(S##regist) + // Sampling functions + #define TORQUE_TEX1D(tex,coords) texture_##tex.Sample(tex,coords) + #define TORQUE_TEX2D(tex,coords) texture_##tex.Sample(tex,coords) + #define TORQUE_TEX2DPROJ(tex,coords) texture_##tex.Sample(tex,coords.xy / coords.w) + #define TORQUE_TEX3D(tex,coords) texture_##tex.Sample(tex,coords) + #define TORQUE_TEXCUBE(tex,coords) texture_##tex.Sample(tex,coords) + // The mipmap LOD is specified in coord.w + #define TORQUE_TEX2DLOD(tex,coords) texture_##tex.SampleLevel(tex,coords.xy,coords.w) + + //helper if you want to pass sampler/texture in a function + //2D + #define TORQUE_SAMPLER2D(tex) Texture2D texture_##tex, SamplerState tex + #define TORQUE_SAMPLER2D_MAKEARG(tex) texture_##tex, tex + //Cube + #define TORQUE_SAMPLERCUBE(tex) TextureCube texture_##tex, SamplerState tex + #define TORQUE_SAMPLERCUBE_MAKEARG(tex) texture_##tex, tex +#endif + +#endif // _TORQUE_SHADERMODEL_ + diff --git a/Templates/BaseGame/game/data/shaders/common/shaderModelAutoGen.hlsl b/Templates/BaseGame/game/data/shaders/common/shaderModelAutoGen.hlsl new file mode 100644 index 000000000..4f2d8803f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/shaderModelAutoGen.hlsl @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2015 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_SHADERMODEL_AUTOGEN_ +#define _TORQUE_SHADERMODEL_AUTOGEN_ + +#include "shadergen:/autogenConditioners.h" + +// Portability helpers for autogenConditioners +#if (TORQUE_SM >= 10 && TORQUE_SM <=30) + #define TORQUE_PREPASS_UNCONDITION(tex, coords) prepassUncondition(tex, coords) +#elif TORQUE_SM >= 40 + #define TORQUE_PREPASS_UNCONDITION(tex, coords) prepassUncondition(tex, texture_##tex, coords) +#endif + +#endif //_TORQUE_SHADERMODEL_AUTOGEN_ diff --git a/Templates/BaseGame/game/data/shaders/common/shdrConsts.h b/Templates/BaseGame/game/data/shaders/common/shdrConsts.h new file mode 100644 index 000000000..8c262b76a --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/shdrConsts.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifdef IN_HLSL + +#define VC_WORLD_PROJ C0 + +#define VC_TEX_TRANS1 C4 +#define VC_LIGHT_TRANS C8 +#define VC_OBJ_TRANS C12 + +#define VC_CUBE_TRANS C16 +#define VC_CUBE_EYE_POS C19 // in cubemap space +#define VC_EYE_POS C20 // in object space +#define VC_MAT_SPECPOWER C21 + +#define VC_FOGDATA C22 + +#define VC_LIGHT_POS1 C23 +#define VC_LIGHT_DIR1 C24 +#define VC_LIGHT_DIFFUSE1 C25 +#define VC_LIGHT_SPEC1 C26 + +#define VC_LIGHT_POS2 C27 +//#define VC_LIGHT_DIR2 C28 +//#define VC_LIGHT_DIFFUSE2 C29 +//#define VC_LIGHT_SPEC2 C30 +#define VC_LIGHT_TRANS2 C31 + +//#define VC_LIGHT_POS4 C35 +//#define VC_LIGHT_DIR4 C36 +//#define VC_LIGHT_DIFFUSE4 C37 +//#define VC_LIGHT_SPEC4 C38 + +#define VC_DETAIL_SCALE C40 + + +#define PC_MAT_SPECCOLOR C0 +#define PC_MAT_SPECPOWER C1 +#define PC_DIFF_COLOR C2 +#define PC_AMBIENT_COLOR C3 +#define PC_ACCUM_TIME C4 +#define PC_DIFF_COLOR2 C5 +#define PC_VISIBILITY C6 +#define PC_COLORMULTIPLY C7 + +#define PC_USERDEF1 C8 + +// Mirror of above. Couldn't be cleaner because HLSL doesn't support function macros +#else + +#define VC_WORLD_PROJ 0 + +#define VC_TEX_TRANS1 4 +#define VC_LIGHT_TRANS 8 +#define VC_OBJ_TRANS 12 + +#define VC_CUBE_TRANS 16 +#define VC_CUBE_EYE_POS 19 // in cubemap space +#define VC_EYE_POS 20 // in object space +#define VC_MAT_SPECPOWER 21 + +#define VC_FOGDATA 22 + +#define VC_LIGHT_POS1 23 +#define VC_LIGHT_DIR1 24 +#define VC_LIGHT_DIFFUSE1 25 +#define VC_LIGHT_SPEC1 26 + +#define VC_LIGHT_POS2 27 +//#define VC_LIGHT_DIR2 28 +//#define VC_LIGHT_DIFFUSE2 29 +//#define VC_LIGHT_SPEC2 30 +#define VC_LIGHT_TRANS2 31 + +//#define VC_LIGHT_POS4 35 +//#define VC_LIGHT_DIR4 36 +//#define VC_LIGHT_DIFFUSE4 37 +//#define VC_LIGHT_SPEC4 38 + +#define VC_DETAIL_SCALE 40 + + + +#define PC_MAT_SPECCOLOR 0 +#define PC_MAT_SPECPOWER 1 +#define PC_DIFF_COLOR 2 +#define PC_AMBIENT_COLOR 3 +#define PC_ACCUM_TIME 4 +#define PC_DIFF_COLOR2 5 +#define PC_VISIBILITY 6 +#define PC_COLORMULTIPLY 7 + +#define PC_USERDEF1 8 + +#endif + + diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/blendP.hlsl b/Templates/BaseGame/game/data/shaders/common/terrain/blendP.hlsl new file mode 100644 index 000000000..aeef9d6e3 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/blendP.hlsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "terrain.hlsl" +#include "../shaderModel.hlsl" + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 layerCoord : TEXCOORD0; + float2 texCoord : TEXCOORD1; +}; + +TORQUE_UNIFORM_SAMPLER2D(layerTex, 0); +TORQUE_UNIFORM_SAMPLER2D(textureMap, 1); + +uniform float texId; +uniform float layerSize; + +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + float4 layerSample = round( TORQUE_TEX2D( layerTex, IN.layerCoord ) * 255.0f ); + + float blend = calcBlend( texId, IN.layerCoord, layerSize, layerSample ); + + clip( blend - 0.0001 ); + + return float4( TORQUE_TEX2D(textureMap, IN.texCoord).rgb, blend); +} diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/blendV.hlsl b/Templates/BaseGame/game/data/shaders/common/terrain/blendV.hlsl new file mode 100644 index 000000000..9ccd33301 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/blendV.hlsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// The vertex shader used in the generation and caching of the +/// base terrain texture. + +#include "../shaderModel.hlsl" + +struct VertData +{ + float3 position : POSITION; + float2 texCoord : TEXCOORD0; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + float2 layerCoord : TEXCOORD0; + float2 texCoord : TEXCOORD1; +}; + +uniform float2 texScale; + +ConnectData main( VertData IN ) +{ + ConnectData OUT; + + OUT.hpos = float4( IN.position, 1 ); + OUT.layerCoord = IN.texCoord; + OUT.texCoord = IN.texCoord * texScale; + + return OUT; +} diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendP.glsl b/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendP.glsl new file mode 100644 index 000000000..3189ea01d --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendP.glsl @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../terrain.glsl" +#include "../../gl/hlslCompat.glsl" + +in vec2 layerCoord; +#define IN_layerCoord layerCoord +in vec2 texCoord; +#define IN_texCoord texCoord + +uniform sampler2D layerTex; +uniform sampler2D textureMap; +uniform float texId; +uniform float layerSize; + +out vec4 OUT_col; + +void main() +{ + vec4 layerSample = round(texture( layerTex, IN_layerCoord ) * 255.0); + + float blend = calcBlend( texId, IN_layerCoord, layerSize, layerSample ); + + if(blend - 0.0001 < 0.0) + discard; + + OUT_col = vec4( texture( textureMap, IN_texCoord ).rgb, blend ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendV.glsl b/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendV.glsl new file mode 100644 index 000000000..dc7b7befa --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/gl/blendV.glsl @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// The vertex shader used in the generation and caching of the +/// base terrain texture. + +in vec4 vPosition; +in vec2 vTexCoord0; + +out vec2 layerCoord; +out vec2 texCoord; + +uniform vec2 texScale; + +void main() +{ + gl_Position = vec4(vPosition.xyz, 1.0); + layerCoord = vTexCoord0.st; + texCoord = vTexCoord0.st * texScale; + + gl_Position.y *= -1; +} diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/terrain.glsl b/Templates/BaseGame/game/data/shaders/common/terrain/terrain.glsl new file mode 100644 index 000000000..756edd553 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/terrain.glsl @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +float calcBlend( float texId, vec2 layerCoord, float layerSize, vec4 layerSample ) +{ + // This is here to disable the blend if none of + // the neighbors equal the current id. + // + // We depend on the input layer samples being + // rounded to the correct integer ids. + // + vec4 diff = clamp( abs( layerSample - texId ), 0.0, 1.0 ); + float noBlend = float(any( bvec4(1 - diff) )); + + // Check if any of the layer samples + // match the current texture id. + vec4 factors = vec4(0); + for(int i = 0; i < 4; i++) + factors[i] = (layerSample[i] == texId) ? 1 : 0; // workaround for Intel + + // This is a custom bilinear filter. + + vec2 uv = layerCoord * layerSize; + vec2 xy = floor( uv ); + vec2 ratio = uv - xy; + vec2 opposite = 1 - ratio; + + float blend = ( factors.b * opposite.x + factors.g * ratio.x ) * opposite.y + + ( factors.r * opposite.x + factors.a * ratio.x ) * ratio.y; + + return noBlend * blend; +} diff --git a/Templates/BaseGame/game/data/shaders/common/terrain/terrain.hlsl b/Templates/BaseGame/game/data/shaders/common/terrain/terrain.hlsl new file mode 100644 index 000000000..b7c87e618 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/terrain/terrain.hlsl @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +float calcBlend( float texId, float2 layerCoord, float layerSize, float4 layerSample ) +{ + // This is here to disable the blend if none of + // the neighbors equal the current id. + // + // We depend on the input layer samples being + // rounded to the correct integer ids. + // + float4 diff = saturate( abs( layerSample - texId ) ); + float noBlend = any( 1 - diff ); + + // Check if any of the layer samples + // match the current texture id. + float4 factors = 0; + [unroll] + for(int i = 0; i < 4; i++) + if(layerSample[i] == texId) + factors[i] = 1; + + // This is a custom bilinear filter. + + float2 uv = layerCoord * layerSize; + float2 xy = floor( uv ); + float2 ratio = uv - xy; + float2 opposite = 1 - ratio; + + // NOTE: This will optimize down to two lerp operations. + float blend = ( factors.b * opposite.x + factors.g * ratio.x ) * opposite.y + + ( factors.r * opposite.x + factors.a * ratio.x ) * ratio.y; + + return noBlend * blend; +} diff --git a/Templates/BaseGame/game/data/shaders/common/torque.hlsl b/Templates/BaseGame/game/data/shaders/common/torque.hlsl new file mode 100644 index 000000000..7081c7153 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/torque.hlsl @@ -0,0 +1,342 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_HLSL_ +#define _TORQUE_HLSL_ + +#include "./shaderModel.hlsl" + +static float M_HALFPI_F = 1.57079632679489661923f; +static float M_PI_F = 3.14159265358979323846f; +static float M_2PI_F = 6.28318530717958647692f; + + +/// Calculate fog based on a start and end positions in worldSpace. +float computeSceneFog( float3 startPos, + float3 endPos, + float fogDensity, + float fogDensityOffset, + float fogHeightFalloff ) +{ + float f = length( startPos - endPos ) - fogDensityOffset; + float h = 1.0 - ( endPos.z * fogHeightFalloff ); + return exp( -fogDensity * f * h ); +} + + +/// Calculate fog based on a start and end position and a height. +/// Positions do not need to be in worldSpace but height does. +float computeSceneFog( float3 startPos, + float3 endPos, + float height, + float fogDensity, + float fogDensityOffset, + float fogHeightFalloff ) +{ + float f = length( startPos - endPos ) - fogDensityOffset; + float h = 1.0 - ( height * fogHeightFalloff ); + return exp( -fogDensity * f * h ); +} + + +/// Calculate fog based on a distance, height is not used. +float computeSceneFog( float dist, float fogDensity, float fogDensityOffset ) +{ + float f = dist - fogDensityOffset; + return exp( -fogDensity * f ); +} + + +/// Convert a float4 uv in viewport space to render target space. +float2 viewportCoordToRenderTarget( float4 inCoord, float4 rtParams ) +{ + float2 outCoord = inCoord.xy / inCoord.w; + outCoord = ( outCoord * rtParams.zw ) + rtParams.xy; + return outCoord; +} + + +/// Convert a float2 uv in viewport space to render target space. +float2 viewportCoordToRenderTarget( float2 inCoord, float4 rtParams ) +{ + float2 outCoord = ( inCoord * rtParams.zw ) + rtParams.xy; + return outCoord; +} + + +/// Convert a float4 quaternion into a 3x3 matrix. +float3x3 quatToMat( float4 quat ) +{ + float xs = quat.x * 2.0f; + float ys = quat.y * 2.0f; + float zs = quat.z * 2.0f; + + float wx = quat.w * xs; + float wy = quat.w * ys; + float wz = quat.w * zs; + + float xx = quat.x * xs; + float xy = quat.x * ys; + float xz = quat.x * zs; + + float yy = quat.y * ys; + float yz = quat.y * zs; + float zz = quat.z * zs; + + float3x3 mat; + + mat[0][0] = 1.0f - (yy + zz); + mat[0][1] = xy - wz; + mat[0][2] = xz + wy; + + mat[1][0] = xy + wz; + mat[1][1] = 1.0f - (xx + zz); + mat[1][2] = yz - wx; + + mat[2][0] = xz - wy; + mat[2][1] = yz + wx; + mat[2][2] = 1.0f - (xx + yy); + + return mat; +} + + +/// The number of additional substeps we take when refining +/// the results of the offset parallax mapping function below. +/// +/// You should turn down the number of steps if your needing +/// more performance out of your parallax surfaces. Increasing +/// the number doesn't yeild much better results and is rarely +/// worth the additional cost. +/// +#define PARALLAX_REFINE_STEPS 3 + +/// Performs fast parallax offset mapping using +/// multiple refinement steps. +/// +/// @param texMap The texture map whos alpha channel we sample the parallax depth. +/// @param texCoord The incoming texture coordinate for sampling the parallax depth. +/// @param negViewTS The negative view vector in tangent space. +/// @param depthScale The parallax factor used to scale the depth result. +/// +float2 parallaxOffset(TORQUE_SAMPLER2D(texMap), float2 texCoord, float3 negViewTS, float depthScale) +{ + float depth = TORQUE_TEX2D(texMap, texCoord).a/(PARALLAX_REFINE_STEPS*2); + float2 offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + TORQUE_TEX2D(texMap, texCoord + offset).a)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS); + } + + return offset; +} + +/// Same as parallaxOffset but for dxtnm where depth is stored in the red channel instead of the alpha +float2 parallaxOffsetDxtnm(TORQUE_SAMPLER2D(texMap), float2 texCoord, float3 negViewTS, float depthScale) +{ + float depth = TORQUE_TEX2D(texMap, texCoord).r/(PARALLAX_REFINE_STEPS*2); + float2 offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS*2); + + for (int i = 0; i < PARALLAX_REFINE_STEPS; i++) + { + depth = (depth + TORQUE_TEX2D(texMap, texCoord + offset).r)/(PARALLAX_REFINE_STEPS*2); + offset = negViewTS.xy * (depth * depthScale)/(PARALLAX_REFINE_STEPS*2); + } + + return offset; +} + + +/// The maximum value for 16bit per component integer HDR encoding. +static const float HDR_RGB16_MAX = 100.0; + +/// The maximum value for 10bit per component integer HDR encoding. +static const float HDR_RGB10_MAX = 4.0; + +/// Encodes an HDR color for storage into a target. +float3 hdrEncode( float3 sample ) +{ + #if defined( TORQUE_HDR_RGB16 ) + + return sample / HDR_RGB16_MAX; + + #elif defined( TORQUE_HDR_RGB10 ) + + return sample / HDR_RGB10_MAX; + + #else + + // No encoding. + return sample; + + #endif +} + +/// Encodes an HDR color for storage into a target. +float4 hdrEncode( float4 sample ) +{ + return float4( hdrEncode( sample.rgb ), sample.a ); +} + +/// Decodes an HDR color from a target. +float3 hdrDecode( float3 sample ) +{ + #if defined( TORQUE_HDR_RGB16 ) + + return sample * HDR_RGB16_MAX; + + #elif defined( TORQUE_HDR_RGB10 ) + + return sample * HDR_RGB10_MAX; + + #else + + // No encoding. + return sample; + + #endif +} + +/// Decodes an HDR color from a target. +float4 hdrDecode( float4 sample ) +{ + return float4( hdrDecode( sample.rgb ), sample.a ); +} + +/// Returns the luminance for an HDR pixel. +float hdrLuminance( float3 sample ) +{ + // There are quite a few different ways to + // calculate luminance from an rgb value. + // + // If you want to use a different technique + // then plug it in here. + // + + //////////////////////////////////////////////////////////////////////////// + // + // Max component luminance. + // + //float lum = max( sample.r, max( sample.g, sample.b ) ); + + //////////////////////////////////////////////////////////////////////////// + // The perceptual relative luminance. + // + // See http://en.wikipedia.org/wiki/Luminance_(relative) + // + const float3 RELATIVE_LUMINANCE = float3( 0.2126, 0.7152, 0.0722 ); + float lum = dot( sample, RELATIVE_LUMINANCE ); + + //////////////////////////////////////////////////////////////////////////// + // + // The average component luminance. + // + //const float3 AVERAGE_LUMINANCE = float3( 0.3333, 0.3333, 0.3333 ); + //float lum = dot( sample, AVERAGE_LUMINANCE ); + + return lum; +} + +/// Called from the visibility feature to do screen +/// door transparency for fading of objects. +void fizzle(float2 vpos, float visibility) +{ + // NOTE: The magic values below are what give us + // the nice even pattern during the fizzle. + // + // These values can be changed to get different + // patterns... some better than others. + // + // Horizontal Blinds - { vpos.x, 0.916, vpos.y, 0 } + // Vertical Lines - { vpos.x, 12.9898, vpos.y, 78.233 } + // + // I'm sure there are many more patterns here to + // discover for different effects. + + float2x2 m = { vpos.x, 0.916, vpos.y, 0.350 }; + clip( visibility - frac( determinant( m ) ) ); +} + +// Deferred Shading: Material Info Flag Check +bool getFlag(float flags, int num) +{ + int process = round(flags * 255); + int squareNum = pow(2, num); + return (fmod(process, pow(2, squareNum)) >= squareNum); +} + +// #define TORQUE_STOCK_GAMMA +#ifdef TORQUE_STOCK_GAMMA +// Sample in linear space. Decodes gamma. +float4 toLinear(float4 tex) +{ + return tex; +} +// Encodes gamma. +float4 toGamma(float4 tex) +{ + return tex; +} +float3 toLinear(float3 tex) +{ + return tex; +} +// Encodes gamma. +float3 toGamma(float3 tex) +{ + return tex; +} +float3 toLinear(float3 tex) +{ + return tex; +} +// Encodes gamma. +float3 toLinear(float3 tex) +{ + return tex; +} +#else +// Sample in linear space. Decodes gamma. +float4 toLinear(float4 tex) +{ + return float4(pow(abs(tex.rgb), 2.2), tex.a); +} +// Encodes gamma. +float4 toGamma(float4 tex) +{ + return float4(pow(abs(tex.rgb), 1.0/2.2), tex.a); +} +// Sample in linear space. Decodes gamma. +float3 toLinear(float3 tex) +{ + return pow(abs(tex.rgb), 2.2); +} +// Encodes gamma. +float3 toGamma(float3 tex) +{ + return pow(abs(tex.rgb), 1.0/2.2); +} +#endif // + +#endif // _TORQUE_HLSL_ diff --git a/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicP.glsl b/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicP.glsl new file mode 100644 index 000000000..1d5a07c3f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicP.glsl @@ -0,0 +1,217 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/torque.glsl" +#include "../../gl/hlslCompat.glsl" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +// miscParams +#define FRESNEL_BIAS miscParams[0] +#define FRESNEL_POWER miscParams[1] +#define CLARITY miscParams[2] +#define ISRIVER miscParams[3] + +// reflectParams +#define REFLECT_PLANE_Z reflectParams[0] +#define REFLECT_MIN_DIST reflectParams[1] +#define REFLECT_MAX_DIST reflectParams[2] +#define NO_REFLECT reflectParams[3] + +// distortionParams +#define DISTORT_START_DIST distortionParams[0] +#define DISTORT_END_DIST distortionParams[1] +#define DISTORT_FULL_DEPTH distortionParams[2] + +// ConnectData.misc +#define LIGHT_VEC IN_misc.xyz +#define WORLD_Z IN_objPos.w + +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +// TexCoord 0 and 1 (xy,zw) for ripple texture lookup +in vec4 rippleTexCoord01; +#define IN_rippleTexCoord01 rippleTexCoord01 + +// TexCoord 2 for ripple texture lookup +in vec2 rippleTexCoord2; +#define IN_rippleTexCoord2 rippleTexCoord2 + +// Screenspace vert position BEFORE wave transformation +in vec4 posPreWave; +#define IN_posPreWave posPreWave + +// Screenspace vert position AFTER wave transformation +in vec4 posPostWave; +#define IN_posPostWave posPostWave + +// Worldspace unit distance/depth of this vertex/pixel +in float pixelDist; +#define IN_pixelDist pixelDist + +in vec4 objPos; +#define IN_objPos objPos + +in vec3 misc; +#define IN_misc misc + +//----------------------------------------------------------------------------- +// approximate Fresnel function +//----------------------------------------------------------------------------- +float fresnel(float NdotV, float bias, float power) +{ + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); +} + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform sampler2D bumpMap; +//uniform sampler2D prepassTex; +uniform sampler2D reflectMap; +uniform sampler2D refractBuff; +uniform samplerCube skyMap; +//uniform sampler2D foamMap; +uniform vec4 baseColor; +uniform vec4 miscParams; +uniform vec4 reflectParams; +uniform vec3 ambientColor; +uniform vec3 eyePos; +uniform vec3 distortionParams; +uniform vec3 fogData; +uniform vec4 fogColor; +uniform vec4 rippleMagnitude; +uniform vec4 specularParams; +uniform mat4 modelMat; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + // Modulate baseColor by the ambientColor. + vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 ); + waterBaseColor = toLinear(waterBaseColor); + + // Get the bumpNorm... + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + + // We subtract a little from it so that we don't + // distort where the water surface intersects the + // camera near plane. + float distortAmt = saturate( IN_pixelDist / 1.0 ) * 0.8; + + vec4 distortPos = IN_posPostWave; + distortPos.xy += bumpNorm.xy * distortAmt; + + #ifdef UNDERWATER + OUT_col = hdrEncode( textureProj( refractBuff, distortPos ) ); + #else + + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + vec3 reflectionVec = reflect( eyeVec, bumpNorm ); + + // Color that replaces the reflection color when we do not + // have one that is appropriate. + vec4 fakeColor = vec4(ambientColor,1); + + // Use fakeColor for ripple-normals that are angled towards the camera + eyeVec = -eyeVec; + eyeVec = normalize( eyeVec ); + float ang = saturate( dot( eyeVec, bumpNorm ) ); + float fakeColorAmt = ang; + + // Get reflection map color + vec4 refMapColor = hdrDecode( textureProj( reflectMap, distortPos ) ); + // If we do not have a reflection texture then we use the cubemap. + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); + + // Combine reflection color and fakeColor. + vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); + //return refMapColor; + + // Get refract color + vec4 refractColor = hdrDecode( textureProj( refractBuff, distortPos ) ); + + // calc "diffuse" color by lerping from the water color + // to refraction image based on the water clarity. + vec4 diffuseColor = mix( refractColor, waterBaseColor, 1.0f - CLARITY ); + + // fresnel calculation + float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + //return vec4( fresnelTerm.rrr, 1 ); + + // Also scale the frensel by our distance to the + // water surface. This removes the hard reflection + // when really close to the water surface. + fresnelTerm *= saturate( IN_pixelDist - 0.1 ); + + // Combine the diffuse color and reflection image via the + // fresnel term and set out output color. + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); + + #ifdef WATER_SPEC + + // Get some specular reflection. + vec3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( bumpNorm ); + half3 halfAng = normalize( eyeVec + -LIGHT_VEC ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx ); + + #else // Disable fogging if spec is on because otherwise we run out of instructions. + + // Fog it. + float factor = computeSceneFog( eyePos, + IN_objPos.xyz, + WORLD_Z, + fogData.x, + fogData.y, + fogData.z ); + + //OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + #endif + + OUT_col = hdrEncode( OUT ); + +#endif +} diff --git a/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicV.glsl b/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicV.glsl new file mode 100644 index 000000000..e92c948e9 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/gl/waterBasicV.glsl @@ -0,0 +1,243 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +// TexCoord 0 and 1 (xy,zw) for ripple texture lookup +out vec4 rippleTexCoord01; +#define OUT_rippleTexCoord01 rippleTexCoord01 + +// TexCoord 2 for ripple texture lookup +out vec2 rippleTexCoord2; +#define OUT_rippleTexCoord2 rippleTexCoord2 + +// Screenspace vert position BEFORE wave transformation +out vec4 posPreWave; +#define OUT_posPreWave posPreWave + +// Screenspace vert position AFTER wave transformation +out vec4 posPostWave; +#define OUT_posPostWave posPostWave + +// Worldspace unit distance/depth of this vertex/pixel +out float pixelDist; +#define OUT_pixelDist pixelDist + +out vec4 objPos; +#define OUT_objPos objPos + +out vec3 misc; +#define OUT_misc misc + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform mat4 modelMat; +uniform mat4 modelview; +uniform vec4 rippleMat[3]; +uniform vec3 eyePos; +uniform vec2 waveDir[3]; +uniform vec2 waveData[3]; +uniform vec2 rippleDir[3]; +uniform vec2 rippleTexScale[3]; +uniform vec3 rippleSpeed; +uniform vec3 inLightVec; +uniform vec3 reflectNormal; +uniform float gridElementSize; +uniform float elapsedTime; +uniform float undulateMaxDist; + +in vec4 vPosition; +in vec3 vNormal; +in vec2 vTexCoord0; +in vec4 vTexCoord1; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec4 IN_position = vPosition; + vec3 IN_normal = vNormal; + vec2 IN_undulateData = vTexCoord0; + vec4 IN_horizonFactor = vTexCoord1; + vec4 OUT_hpos = vec4(0); + + // use projection matrix for reflection / refraction texture coords + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); + + // Move the vertex based on the horizonFactor if specified to do so for this vert. + // if ( IN_horizonFactor.z > 0 ) + // { + // vec2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; + // IN_position.xy += offsetXY; + // IN_undulateData += offsetXY; + // } + + vec4 worldPos = tMul( modelMat, IN_position ); + + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); + + //OUT_objPos = worldPos; + OUT_objPos.xyz = IN_position.xyz; + OUT_objPos.w = worldPos.z; + + // Send pre-undulation screenspace position + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); + + // Calculate the undulation amount for this vertex. + vec2 undulatePos = tMul( modelMat, vec4( IN_undulateData.xy, 0, 1 ) ).xy; + //if ( undulatePos.x < 0 ) + // undulatePos = IN_position.xy; + + float undulateAmt = 0.0; + + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN_position.xyz, eyePos ); + dist = clamp( dist, 1.0, undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); + + // Also scale down wave magnitude if the camera is very very close. + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + //#endif + //undulateAmt = 0; + + // Apply wave undulation to the vertex. + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; + + // Save worldSpace position of this pixel/vert + //OUT_worldPos = OUT_posPostWave.xyz; + //OUT_worldPos = tMul( modelMat, OUT_posPostWave.xyz ); + //OUT_worldPos.z += objTrans[2][2]; //91.16; + + // OUT_misc.w = tMul( modelMat, OUT_fogPos ).z; + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_fogPos.xyz - eyePos ); + // OUT_fogPos.xy += awayVec.xy * 1000.0; + // } + + // Convert to screen + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); // tMul( modelview, vec4( OUT_posPostWave.xyz, 1 ) ); + + // Setup the OUT position symantic variable + OUT_hpos = OUT_posPostWave; // tMul( modelview, vec4( IN_position.xyz, 1 ) ); //vec4( OUT_posPostWave.xyz, 1 ); + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); + + // Save world space camera dist/depth of the outgoing pixel + OUT_pixelDist = OUT_hpos.z; + + // Convert to reflection texture space + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); + + vec2 txPos = undulatePos; + if ( bool(IN_horizonFactor.x) ) + txPos = normalize( txPos ) * 50000.0; + + // set up tex coordinates for the 3 interacting normal maps + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + mat2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[1][0] = rippleMat[0].y; + texMat[0][1] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); + + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[1][0] = rippleMat[1].y; + texMat[0][1] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); + + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + + texMat[0][0] = rippleMat[2].x; + texMat[1][0] = rippleMat[2].y; + texMat[0][1] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); + +#ifdef WATER_SPEC + + vec3 binormal = vec3( 1, 0, 0 ); + vec3 tangent = vec3( 0, 1, 0 ); + vec3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); + } + + binormal = normalize( binormal ); + tangent = normalize( tangent ); + normal = cross( binormal, tangent ); + + mat3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + worldToTangent = transpose(worldToTangent); + + OUT_misc.xyz = tMul( inLightVec, modelMat ); + OUT_misc.xyz = tMul( worldToTangent, OUT_misc.xyz ); + +#else + + OUT_misc.xyz = inLightVec; + +#endif + + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/water/gl/waterP.glsl b/Templates/BaseGame/game/data/shaders/common/water/gl/waterP.glsl new file mode 100644 index 000000000..5f722282c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/gl/waterP.glsl @@ -0,0 +1,397 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" +#include "../../gl/torque.glsl" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +#define PIXEL_DIST IN_rippleTexCoord2.z +// miscParams +#define FRESNEL_BIAS miscParams[0] +#define FRESNEL_POWER miscParams[1] +// miscParams[2] is unused +#define ISRIVER miscParams[3] + +// reflectParams +#define REFLECT_PLANE_Z reflectParams[0] +#define REFLECT_MIN_DIST reflectParams[1] +#define REFLECT_MAX_DIST reflectParams[2] +#define NO_REFLECT reflectParams[3] + +// fogParams +#define FOG_DENSITY fogParams[0] +#define FOG_DENSITY_OFFSET fogParams[1] + +// wetnessParams +#define WET_DEPTH wetnessParams[0] +#define WET_COLOR_FACTOR wetnessParams[1] + +// distortionParams +#define DISTORT_START_DIST distortionParams[0] +#define DISTORT_END_DIST distortionParams[1] +#define DISTORT_FULL_DEPTH distortionParams[2] + +// foamParams +#define FOAM_OPACITY foamParams[0] +#define FOAM_MAX_DEPTH foamParams[1] +#define FOAM_AMBIENT_LERP foamParams[2] +#define FOAM_RIPPLE_INFLUENCE foamParams[3] + +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +//ConnectData IN + +in vec4 hpos; + +// TexCoord 0 and 1 (xy,zw) for ripple texture lookup +in vec4 rippleTexCoord01; + +// xy is TexCoord 2 for ripple texture lookup +// z is the Worldspace unit distance/depth of this vertex/pixel +// w is amount of the crestFoam ( more at crest of waves ). +in vec4 rippleTexCoord2; + +// Screenspace vert position BEFORE wave transformation +in vec4 posPreWave; + +// Screenspace vert position AFTER wave transformation +in vec4 posPostWave; + +// Objectspace vert position BEFORE wave transformation +// w coord is world space z position. +in vec4 objPos; + +in vec4 foamTexCoords; + +in mat3 tangentMat; + + +#define IN_hpos hpos +#define IN_rippleTexCoord01 rippleTexCoord01 +#define IN_rippleTexCoord2 rippleTexCoord2 +#define IN_posPreWave posPreWave +#define IN_posPostWave posPostWave +#define IN_objPos objPos +#define IN_foamTexCoords foamTexCoords +#define IN_tangentMat tangentMat + +//----------------------------------------------------------------------------- +// approximate Fresnel function +//----------------------------------------------------------------------------- +float fresnel(float NdotV, float bias, float power) +{ + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); +} + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform sampler2D bumpMap; +uniform sampler2D prepassTex; +uniform sampler2D reflectMap; +uniform sampler2D refractBuff; +uniform samplerCube skyMap; +uniform sampler2D foamMap; +uniform sampler1D depthGradMap; +uniform vec4 specularParams; +uniform vec4 baseColor; +uniform vec4 miscParams; +uniform vec2 fogParams; +uniform vec4 reflectParams; +uniform vec3 reflectNormal; +uniform vec2 wetnessParams; +uniform float farPlaneDist; +uniform vec3 distortionParams; +uniform vec4 foamParams; +uniform vec3 ambientColor; +uniform vec3 eyePos; // This is in object space! +uniform vec3 fogData; +uniform vec4 fogColor; +uniform vec4 rippleMagnitude; +uniform vec4 rtParams1; +uniform float depthGradMax; +uniform vec3 inLightVec; +uniform mat4 modelMat; +uniform vec4 sunColor; +uniform float sunBrightness; +uniform float reflectivity; + +out vec4 OUT_col; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + // Get the bumpNorm... + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + bumpNorm = tMul( bumpNorm, IN_tangentMat ); + + // Get depth of the water surface (this pixel). + // Convert from WorldSpace to EyeSpace. + float pixelDepth = PIXEL_DIST / farPlaneDist; + + vec2 prepassCoord = viewportCoordToRenderTarget( IN_posPostWave, rtParams1 ); + + float startDepth = prepassUncondition( prepassTex, prepassCoord ).w; + + // The water depth in world units of the undistorted pixel. + float startDelta = ( startDepth - pixelDepth ); + startDelta = max( startDelta, 0.0 ); + startDelta *= farPlaneDist; + + // Calculate the distortion amount for the water surface. + // + // We subtract a little from it so that we don't + // distort where the water surface intersects the + // camera near plane. + float distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); + + // Scale down distortion in shallow water. + distortAmt *= saturate( startDelta / DISTORT_FULL_DEPTH ); + + // Do the intial distortion... we might remove it below. + vec2 distortDelta = bumpNorm.xy * distortAmt; + vec4 distortPos = IN_posPostWave; + distortPos.xy += distortDelta; + + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + // Get prepass depth at the position of this distorted pixel. + float prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; + + float delta = ( prepassDepth - pixelDepth ) * farPlaneDist; + + if ( delta < 0.0 ) + { + // If we got a negative delta then the distorted + // sample is above the water surface. Mask it out + // by removing the distortion. + distortPos = IN_posPostWave; + delta = startDelta; + distortAmt = 0; + } + else + { + float diff = ( prepassDepth - startDepth ) * farPlaneDist; + + if ( diff < 0 ) + { + distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); + distortAmt *= saturate( delta / DISTORT_FULL_DEPTH ); + + distortDelta = bumpNorm.xy * distortAmt; + + distortPos = IN_posPostWave; + distortPos.xy += distortDelta; + + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + // Get prepass depth at the position of this distorted pixel. + prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; + delta = ( prepassDepth - pixelDepth ) * farPlaneDist; + } + + if ( delta < 0.1 ) + { + // If we got a negative delta then the distorted + // sample is above the water surface. Mask it out + // by removing the distortion. + distortPos = IN_posPostWave; + delta = startDelta; + distortAmt = 0; + } + } + + vec4 temp = IN_posPreWave; + temp.xy += bumpNorm.xy * distortAmt; + vec2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams1 ); + + vec2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + vec4 fakeColor = vec4(ambientColor,1); + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + eyeVec = tMul( IN_tangentMat, eyeVec ); + vec3 reflectionVec = reflect( eyeVec, bumpNorm ); + + // Use fakeColor for ripple-normals that are angled towards the camera + eyeVec = -eyeVec; + eyeVec = normalize( eyeVec ); + float ang = saturate( dot( eyeVec, bumpNorm ) ); + float fakeColorAmt = ang; + + // for verts far from the reflect plane z position + float rplaneDist = abs( REFLECT_PLANE_Z - IN_objPos.w ); + rplaneDist = saturate( ( rplaneDist - 1.0 ) / 2.0 ); + rplaneDist *= ISRIVER; + fakeColorAmt = max( fakeColorAmt, rplaneDist ); + +#ifndef UNDERWATER + + // Get foam color and amount + vec2 foamRippleOffset = bumpNorm.xy * FOAM_RIPPLE_INFLUENCE; + vec4 IN_foamTexCoords = IN_foamTexCoords; + IN_foamTexCoords.xy += foamRippleOffset; + IN_foamTexCoords.zw += foamRippleOffset; + + vec4 foamColor = texture( foamMap, IN_foamTexCoords.xy ); + foamColor += texture( foamMap, IN_foamTexCoords.zw ); + foamColor = saturate( foamColor ); + + // Modulate foam color by ambient color + // so we don't have glowing white foam at night. + foamColor.rgb = mix( foamColor.rgb, ambientColor.rgb, FOAM_AMBIENT_LERP ); + + float foamDelta = saturate( delta / FOAM_MAX_DEPTH ); + float foamAmt = 1 - pow( foamDelta, 2 ); + + // Fade out the foam in very very low depth, + // this improves the shoreline a lot. + float diff = 0.8 - foamAmt; + if ( diff < 0.0 ) + foamAmt -= foamAmt * abs( diff ) / 0.2; + + foamAmt *= FOAM_OPACITY * foamColor.a; + + foamColor.rgb *= FOAM_OPACITY * foamAmt * foamColor.a; + + // Get reflection map color. + vec4 refMapColor = texture( reflectMap, reflectCoord ); + + // If we do not have a reflection texture then we use the cubemap. + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); + + fakeColor = ( texture( skyMap, reflectionVec ) ); + fakeColor.a = 1; + // Combine reflection color and fakeColor. + vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); + + // Get refract color + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); + + // We darken the refraction color a bit to make underwater + // elements look wet. We fade out this darkening near the + // surface in order to not have hard water edges. + // @param WET_DEPTH The depth in world units at which full darkening will be recieved. + // @param WET_COLOR_FACTOR The refract color is scaled down by this amount when at WET_DEPTH + refractColor.rgb *= 1.0f - ( saturate( delta / WET_DEPTH ) * WET_COLOR_FACTOR ); + + // Add Water fog/haze. + float fogDelta = delta - FOG_DENSITY_OFFSET; + + if ( fogDelta < 0.0 ) + fogDelta = 0.0; + float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * fogDelta ) ); + + // Calculate the water "base" color based on depth. + vec4 waterBaseColor = baseColor * texture( depthGradMap, saturate( delta / depthGradMax ) ); + waterBaseColor = toLinear(waterBaseColor); + + // Modulate baseColor by the ambientColor. + waterBaseColor *= vec4( ambientColor.rgb, 1 ); + + // calc "diffuse" color by lerping from the water color + // to refraction image based on the water clarity. + vec4 diffuseColor = mix( refractColor, waterBaseColor, fogAmt ); + + // fresnel calculation + float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + + // Scale the frensel strength by fog amount + // so that parts that are very clear get very little reflection. + fresnelTerm *= fogAmt; + + // Also scale the frensel by our distance to the + // water surface. This removes the hard reflection + // when really close to the water surface. + fresnelTerm *= saturate( PIXEL_DIST - 0.1 ); + + fresnelTerm *= reflectivity; + + // Combine the diffuse color and reflection image via the + // fresnel term and set out output color. + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); + + vec3 lightVec = inLightVec; + + // Get some specular reflection. + vec3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( newbump ); + vec3 halfAng = normalize( eyeVec + -lightVec ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + // Scale down specularity in very shallow water to improve the transparency of the shoreline. + specular *= saturate( delta / 2 ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * vec3(specular) ); + +#else + + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); + vec4 OUT = refractColor; + +#endif + +#ifndef UNDERWATER + + OUT.rgb = OUT.rgb + foamColor.rgb; + + float factor = computeSceneFog( eyePos, + IN_objPos.xyz, + IN_objPos.w, + fogData.x, + fogData.y, + fogData.z ); + + OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + //OUT.rgb = fogColor.rgb; + +#endif + + OUT.a = 1.0; + + //return OUT; + + OUT_col = hdrEncode( OUT ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/water/gl/waterV.glsl b/Templates/BaseGame/game/data/shaders/common/water/gl/waterV.glsl new file mode 100644 index 000000000..490af63a7 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/gl/waterV.glsl @@ -0,0 +1,241 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../../gl/hlslCompat.glsl" +#include "shadergen:/autogenConditioners.h" + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + vec4 position ;// POSITION; + vec3 normal ;// NORMAL; + vec2 undulateData ;// TEXCOORD0; + vec4 horizonFactor ;// TEXCOORD1; +}; + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- +//VertData IN +in vec4 vPosition; +in vec3 vNormal; +in vec2 vTexCoord0; +in vec4 vTexCoord1; + +#define IN_position_ vPosition +#define IN_normal vNormal +#define IN_undulateData vTexCoord0 +#define IN_horizonFactor vTexCoord1 + +//ConnectData OUT +// + out vec4 hpos ; + +// TexCoord 0 and 1 (xy,zw) for ripple texture lookup +out vec4 rippleTexCoord01; + + // xy is TexCoord 2 for ripple texture lookup + // z is the Worldspace unit distance/depth of this vertex/pixel + // w is amount of the crestFoam ( more at crest of waves ). + out vec4 rippleTexCoord2 ; + +// Screenspace vert position BEFORE wave transformation +out vec4 posPreWave; + +// Screenspace vert position AFTER wave transformation +out vec4 posPostWave; + + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + out vec4 objPos ; + + out vec4 foamTexCoords ; + + out mat3 tangentMat ; +// + +#define OUT_hpos hpos +#define OUT_rippleTexCoord01 rippleTexCoord01 +#define OUT_rippleTexCoord2 rippleTexCoord2 +#define OUT_posPreWave posPreWave +#define OUT_posPostWave posPostWave +#define OUT_objPos objPos +#define OUT_foamTexCoords foamTexCoords +#define OUT_tangentMat tangentMat + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform mat4 modelMat; +uniform mat4 modelview; +uniform vec4 rippleMat[3]; +uniform vec3 eyePos; +uniform vec2 waveDir[3]; +uniform vec2 waveData[3]; +uniform vec2 rippleDir[3]; +uniform vec2 rippleTexScale[3]; +uniform vec3 rippleSpeed; +uniform vec4 foamDir; +uniform vec4 foamTexScale; +uniform vec2 foamSpeed; +uniform vec3 inLightVec; +uniform float gridElementSize; +uniform float elapsedTime; +uniform float undulateMaxDist; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +void main() +{ + vec4 IN_position = IN_position_; + + // use projection matrix for reflection / refraction texture coords + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); + + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); + + OUT_objPos = IN_position; + OUT_objPos.w = tMul( modelMat, IN_position ).z; + + // Send pre-undulation screenspace position + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); + + // Calculate the undulation amount for this vertex. + vec2 undulatePos = tMul( modelMat, vec4 ( IN_undulateData.xy, 0, 1 ) ).xy; + float undulateAmt = 0.0; + + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN_position.xyz, eyePos ); + dist = clamp( dist, 1.0, undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); + + // Also scale down wave magnitude if the camera is very very close. + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + OUT_rippleTexCoord2.w = undulateAmt / ( waveData[0].y + waveData[1].y + waveData[2].y ); + OUT_rippleTexCoord2.w = saturate( OUT_rippleTexCoord2.w - 0.2 ) / 0.8; + + // Apply wave undulation to the vertex. + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; + + // Convert to screen + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); + + // Setup the OUT position symantic variable + OUT_hpos = OUT_posPostWave; + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); + + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_objPos.xyz - eyePos ); + // OUT_objPos.xy += awayVec.xy * 1000.0; + // } + + // Save world space camera dist/depth of the outgoing pixel + OUT_rippleTexCoord2.z = OUT_hpos.z; + + // Convert to reflection texture space + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); + + vec2 txPos = undulatePos; + if ( bool(IN_horizonFactor.x) ) + txPos = normalize( txPos ) * 50000.0; + + // set up tex coordinates for the 3 interacting normal maps + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + mat2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[1][0] = rippleMat[0].y; + texMat[0][1] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); + + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[1][0] = rippleMat[1].y; + texMat[0][1] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); + + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + + texMat[0][0] = rippleMat[2].x; + texMat[1][0] = rippleMat[2].y; + texMat[0][1] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); + + OUT_foamTexCoords.xy = txPos * foamTexScale.xy + foamDir.xy * foamSpeed.x * elapsedTime; + OUT_foamTexCoords.zw = txPos * foamTexScale.zw + foamDir.zw * foamSpeed.y * elapsedTime; + + + vec3 binormal = vec3 ( 1, 0, 0 ); + vec3 tangent = vec3 ( 0, 1, 0 ); + vec3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + } + + binormal = binormal; + tangent = tangent; + normal = cross( binormal, tangent ); + + mat3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + OUT_tangentMat = transpose(worldToTangent); + + gl_Position = OUT_hpos; + correctSSP(gl_Position); +} + diff --git a/Templates/BaseGame/game/data/shaders/common/water/waterBasicP.hlsl b/Templates/BaseGame/game/data/shaders/common/water/waterBasicP.hlsl new file mode 100644 index 000000000..efb437779 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/waterBasicP.hlsl @@ -0,0 +1,214 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../torque.hlsl" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +// miscParams +#define FRESNEL_BIAS miscParams[0] +#define FRESNEL_POWER miscParams[1] +#define CLARITY miscParams[2] +#define ISRIVER miscParams[3] + +// reflectParams +#define REFLECT_PLANE_Z reflectParams[0] +#define REFLECT_MIN_DIST reflectParams[1] +#define REFLECT_MAX_DIST reflectParams[2] +#define NO_REFLECT reflectParams[3] + +// distortionParams +#define DISTORT_START_DIST distortionParams[0] +#define DISTORT_END_DIST distortionParams[1] +#define DISTORT_FULL_DEPTH distortionParams[2] + +// ConnectData.misc +#define LIGHT_VEC IN.misc.xyz +#define WORLD_Z IN.objPos.w + +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup + float4 rippleTexCoord01 : TEXCOORD0; + + // TexCoord 2 for ripple texture lookup + float2 rippleTexCoord2 : TEXCOORD1; + + // Screenspace vert position BEFORE wave transformation + float4 posPreWave : TEXCOORD2; + + // Screenspace vert position AFTER wave transformation + float4 posPostWave : TEXCOORD3; + + // Worldspace unit distance/depth of this vertex/pixel + float pixelDist : TEXCOORD4; + + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + float4 objPos : TEXCOORD5; + + float3 misc : TEXCOORD6; +}; + +//----------------------------------------------------------------------------- +// approximate Fresnel function +//----------------------------------------------------------------------------- +float fresnel(float NdotV, float bias, float power) +{ + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); +} + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +TORQUE_UNIFORM_SAMPLER2D(bumpMap,0); +//uniform sampler2D prepassTex : register( S1 ); +TORQUE_UNIFORM_SAMPLER2D(reflectMap,2); +TORQUE_UNIFORM_SAMPLER2D(refractBuff,3); +TORQUE_UNIFORM_SAMPLERCUBE(skyMap,4); +//uniform sampler foamMap : register( S5 ); +uniform float4 baseColor; +uniform float4 miscParams; +uniform float4 reflectParams; +uniform float3 ambientColor; +uniform float3 eyePos; +uniform float3 distortionParams; +uniform float3 fogData; +uniform float4 fogColor; +uniform float4 rippleMagnitude; +uniform float4 specularParams; +uniform float4x4 modelMat; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + // Modulate baseColor by the ambientColor. + float4 waterBaseColor = baseColor * float4( ambientColor.rgb, 1 ); + waterBaseColor = toLinear(waterBaseColor); + + // Get the bumpNorm... + float3 bumpNorm = ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = lerp( bumpNorm, float3(0,0,1), 1.0 - rippleMagnitude.w ); + + // We subtract a little from it so that we don't + // distort where the water surface intersects the + // camera near plane. + float distortAmt = saturate( IN.pixelDist / 1.0 ) * 0.8; + + float4 distortPos = IN.posPostWave; + distortPos.xy += bumpNorm.xy * distortAmt; + + #ifdef UNDERWATER + return hdrEncode( TORQUE_TEX2DPROJ( refractBuff, distortPos ) ); + #else + + float3 eyeVec = IN.objPos.xyz - eyePos; + eyeVec = mul( (float3x3)modelMat, eyeVec ); + float3 reflectionVec = reflect( eyeVec, bumpNorm ); + + // Color that replaces the reflection color when we do not + // have one that is appropriate. + float4 fakeColor = float4(ambientColor,1); + + // Use fakeColor for ripple-normals that are angled towards the camera + eyeVec = -eyeVec; + eyeVec = normalize( eyeVec ); + float ang = saturate( dot( eyeVec, bumpNorm ) ); + float fakeColorAmt = ang; + + // Get reflection map color + float4 refMapColor = hdrDecode( TORQUE_TEX2DPROJ( reflectMap, distortPos ) ); + // If we do not have a reflection texture then we use the cubemap. + refMapColor = lerp( refMapColor, TORQUE_TEXCUBE( skyMap, reflectionVec ), NO_REFLECT ); + + // Combine reflection color and fakeColor. + float4 reflectColor = lerp( refMapColor, fakeColor, fakeColorAmt ); + //return refMapColor; + + // Get refract color + float4 refractColor = hdrDecode( TORQUE_TEX2DPROJ( refractBuff, distortPos ) ); + + // calc "diffuse" color by lerping from the water color + // to refraction image based on the water clarity. + float4 diffuseColor = lerp( refractColor, waterBaseColor, 1.0f - CLARITY ); + + // fresnel calculation + float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + //return float4( fresnelTerm.rrr, 1 ); + + // Also scale the frensel by our distance to the + // water surface. This removes the hard reflection + // when really close to the water surface. + fresnelTerm *= saturate( IN.pixelDist - 0.1 ); + + // Combine the diffuse color and reflection image via the + // fresnel term and set out output color. + float4 OUT = lerp( diffuseColor, reflectColor, fresnelTerm ); + + #ifdef WATER_SPEC + + // Get some specular reflection. + float3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( bumpNorm ); + half3 halfAng = normalize( eyeVec + -LIGHT_VEC ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx ); + + #else // Disable fogging if spec is on because otherwise we run out of instructions. + + // Fog it. + float factor = computeSceneFog( eyePos, + IN.objPos.xyz, + WORLD_Z, + fogData.x, + fogData.y, + fogData.z ); + + //OUT.rgb = lerp( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + #endif + + return hdrEncode( OUT ); + +#endif +} diff --git a/Templates/BaseGame/game/data/shaders/common/water/waterBasicV.hlsl b/Templates/BaseGame/game/data/shaders/common/water/waterBasicV.hlsl new file mode 100644 index 000000000..310647c90 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/waterBasicV.hlsl @@ -0,0 +1,237 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + float3 position : POSITION; + float3 normal : NORMAL; + float2 undulateData : TEXCOORD0; + float4 horizonFactor : TEXCOORD1; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup + float4 rippleTexCoord01 : TEXCOORD0; + + // TexCoord 2 for ripple texture lookup + float2 rippleTexCoord2 : TEXCOORD1; + + // Screenspace vert position BEFORE wave transformation + float4 posPreWave : TEXCOORD2; + + // Screenspace vert position AFTER wave transformation + float4 posPostWave : TEXCOORD3; + + // Worldspace unit distance/depth of this vertex/pixel + float pixelDist : TEXCOORD4; + + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + float4 objPos : TEXCOORD5; + + float3 misc : TEXCOORD6; +}; + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform float4x4 modelMat; +uniform float4x4 modelview; +uniform float4 rippleMat[3]; +uniform float3 eyePos; +uniform float2 waveDir[3]; +uniform float2 waveData[3]; +uniform float2 rippleDir[3]; +uniform float2 rippleTexScale[3]; +uniform float3 rippleSpeed; +uniform float3 inLightVec; +uniform float3 reflectNormal; +uniform float gridElementSize; +uniform float elapsedTime; +uniform float undulateMaxDist; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertData IN ) +{ + ConnectData OUT; + + // use projection matrix for reflection / refraction texture coords + float4x4 texGen = { 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 }; + + // Move the vertex based on the horizonFactor if specified to do so for this vert. + // if ( IN.horizonFactor.z > 0 ) + // { + // float2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; + // IN.position.xy += offsetXY; + // IN.undulateData += offsetXY; + // } + float4 inPos = float4(IN.position, 1.0); + float4 worldPos = mul(modelMat, inPos); + + IN.position.z = lerp( IN.position.z, eyePos.z, IN.horizonFactor.x ); + + //OUT.objPos = worldPos; + OUT.objPos.xyz = IN.position; + OUT.objPos.w = worldPos.z; + + // Send pre-undulation screenspace position + OUT.posPreWave = mul( modelview, inPos ); + OUT.posPreWave = mul( texGen, OUT.posPreWave ); + + // Calculate the undulation amount for this vertex. + float2 undulatePos = mul( modelMat, float4( IN.undulateData.xy, 0, 1 )).xy; + //if ( undulatePos.x < 0 ) + // undulatePos = IN.position.xy; + + float undulateAmt = 0.0; + + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN.position, eyePos ); + dist = clamp( dist, 1.0, undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); + + // Also scale down wave magnitude if the camera is very very close. + undulateFade *= saturate( ( distance( IN.position, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + //#endif + //undulateAmt = 0; + + // Apply wave undulation to the vertex. + OUT.posPostWave = inPos; + OUT.posPostWave.xyz += IN.normal.xyz * undulateAmt; + + // Save worldSpace position of this pixel/vert + //OUT.worldPos = OUT.posPostWave.xyz; + //OUT.worldPos = mul( modelMat, OUT.posPostWave.xyz ); + //OUT.worldPos.z += objTrans[2][2]; //91.16; + + // OUT.misc.w = mul( modelMat, OUT.fogPos ).z; + // if ( IN.horizonFactor.x > 0 ) + // { + // float3 awayVec = normalize( OUT.fogPos.xyz - eyePos ); + // OUT.fogPos.xy += awayVec.xy * 1000.0; + // } + + // Convert to screen + OUT.posPostWave = mul( modelview, OUT.posPostWave ); // mul( modelview, float4( OUT.posPostWave.xyz, 1 ) ); + + // Setup the OUT position symantic variable + OUT.hpos = OUT.posPostWave; // mul( modelview, float4( IN.position.xyz, 1 ) ); //float4( OUT.posPostWave.xyz, 1 ); + //OUT.hpos.z = lerp( OUT.hpos.z, OUT.hpos.w, IN.horizonFactor.x ); + + // Save world space camera dist/depth of the outgoing pixel + OUT.pixelDist = OUT.hpos.z; + + // Convert to reflection texture space + OUT.posPostWave = mul( texGen, OUT.posPostWave ); + + float2 txPos = undulatePos; + if ( IN.horizonFactor.x ) + txPos = normalize( txPos ) * 50000.0; + + // set up tex coordinates for the 3 interacting normal maps + OUT.rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT.rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + float2x2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[0][1] = rippleMat[0].y; + texMat[1][0] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT.rippleTexCoord01.xy = mul( texMat, OUT.rippleTexCoord01.xy ); + + OUT.rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT.rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[0][1] = rippleMat[1].y; + texMat[1][0] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT.rippleTexCoord01.zw = mul( texMat, OUT.rippleTexCoord01.zw ); + + OUT.rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT.rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + + texMat[0][0] = rippleMat[2].x; + texMat[0][1] = rippleMat[2].y; + texMat[1][0] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT.rippleTexCoord2.xy = mul( texMat, OUT.rippleTexCoord2.xy ); + +#ifdef WATER_SPEC + + float3 binormal = float3( 1, 0, 0 ); + float3 tangent = float3( 0, 1, 0 ); + float3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * IN.undulateData.x + waveDir[i].y * IN.undulateData.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * IN.undulateData.x + waveDir[i].y * IN.undulateData.y + elapsedTime * waveData[i].x ); + } + + binormal = normalize( binormal ); + tangent = normalize( tangent ); + normal = cross( binormal, tangent ); + + float3x3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + OUT.misc.xyz = mul( inLightVec, modelMat ); + OUT.misc.xyz = mul( worldToTangent, OUT.misc.xyz ); + +#else + + OUT.misc.xyz = inLightVec; + +#endif + + return OUT; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/water/waterP.hlsl b/Templates/BaseGame/game/data/shaders/common/water/waterP.hlsl new file mode 100644 index 000000000..d50c0b51c --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/waterP.hlsl @@ -0,0 +1,384 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModelAutoGen.hlsl" +#include "../torque.hlsl" + +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + +#define PIXEL_DIST IN.rippleTexCoord2.z +// miscParams +#define FRESNEL_BIAS miscParams[0] +#define FRESNEL_POWER miscParams[1] +// miscParams[2] is unused +#define ISRIVER miscParams[3] + +// reflectParams +#define REFLECT_PLANE_Z reflectParams[0] +#define REFLECT_MIN_DIST reflectParams[1] +#define REFLECT_MAX_DIST reflectParams[2] +#define NO_REFLECT reflectParams[3] + +// fogParams +#define FOG_DENSITY fogParams[0] +#define FOG_DENSITY_OFFSET fogParams[1] + +// wetnessParams +#define WET_DEPTH wetnessParams[0] +#define WET_COLOR_FACTOR wetnessParams[1] + +// distortionParams +#define DISTORT_START_DIST distortionParams[0] +#define DISTORT_END_DIST distortionParams[1] +#define DISTORT_FULL_DEPTH distortionParams[2] + +// foamParams +#define FOAM_OPACITY foamParams[0] +#define FOAM_MAX_DEPTH foamParams[1] +#define FOAM_AMBIENT_LERP foamParams[2] +#define FOAM_RIPPLE_INFLUENCE foamParams[3] + +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup + float4 rippleTexCoord01 : TEXCOORD0; + + // xy is TexCoord 2 for ripple texture lookup + // z is the Worldspace unit distance/depth of this vertex/pixel + // w is amount of the crestFoam ( more at crest of waves ). + float4 rippleTexCoord2 : TEXCOORD1; + + // Screenspace vert position BEFORE wave transformation + float4 posPreWave : TEXCOORD2; + + // Screenspace vert position AFTER wave transformation + float4 posPostWave : TEXCOORD3; + + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + float4 objPos : TEXCOORD4; + + float4 foamTexCoords : TEXCOORD5; + + float3x3 tangentMat : TEXCOORD6; +}; + +//----------------------------------------------------------------------------- +// approximate Fresnel function +//----------------------------------------------------------------------------- +float fresnel(float NdotV, float bias, float power) +{ + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); +} + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +TORQUE_UNIFORM_SAMPLER2D(bumpMap,0); +TORQUE_UNIFORM_SAMPLER2D(prepassTex, 1); +TORQUE_UNIFORM_SAMPLER2D(reflectMap, 2); +TORQUE_UNIFORM_SAMPLER2D(refractBuff, 3); +TORQUE_UNIFORM_SAMPLERCUBE(skyMap, 4); +TORQUE_UNIFORM_SAMPLER2D(foamMap, 5); +TORQUE_UNIFORM_SAMPLER1D(depthGradMap, 6); +uniform float4 specularParams; +uniform float4 baseColor; +uniform float4 miscParams; +uniform float2 fogParams; +uniform float4 reflectParams; +uniform float3 reflectNormal; +uniform float2 wetnessParams; +uniform float farPlaneDist; +uniform float3 distortionParams; +uniform float4 foamParams; +uniform float3 ambientColor; +uniform float3 eyePos; // This is in object space! +uniform float3 fogData; +uniform float4 fogColor; +uniform float4 rippleMagnitude; +uniform float4 rtParams1; +uniform float depthGradMax; +uniform float3 inLightVec; +uniform float4x4 modelMat; +uniform float4 sunColor; +uniform float sunBrightness; +uniform float reflectivity; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +float4 main( ConnectData IN ) : TORQUE_TARGET0 +{ + // Get the bumpNorm... + float3 bumpNorm = ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( TORQUE_TEX2D( bumpMap, IN.rippleTexCoord2.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = lerp( bumpNorm, float3(0,0,1), 1.0 - rippleMagnitude.w ); + bumpNorm = mul( bumpNorm, IN.tangentMat ); + + // Get depth of the water surface (this pixel). + // Convert from WorldSpace to EyeSpace. + float pixelDepth = PIXEL_DIST / farPlaneDist; + + float2 prepassCoord = viewportCoordToRenderTarget( IN.posPostWave, rtParams1 ); + + float startDepth = TORQUE_PREPASS_UNCONDITION( prepassTex, prepassCoord ).w; + + // The water depth in world units of the undistorted pixel. + float startDelta = ( startDepth - pixelDepth ); + startDelta = max( startDelta, 0.0 ); + startDelta *= farPlaneDist; + + // Calculate the distortion amount for the water surface. + // + // We subtract a little from it so that we don't + // distort where the water surface intersects the + // camera near plane. + float distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); + + // Scale down distortion in shallow water. + distortAmt *= saturate( startDelta / DISTORT_FULL_DEPTH ); + + // Do the intial distortion... we might remove it below. + float2 distortDelta = bumpNorm.xy * distortAmt; + float4 distortPos = IN.posPostWave; + distortPos.xy += distortDelta; + + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + // Get prepass depth at the position of this distorted pixel. + float prepassDepth = TORQUE_PREPASS_UNCONDITION( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; + + float delta = ( prepassDepth - pixelDepth ) * farPlaneDist; + + if ( delta < 0.0 ) + { + // If we got a negative delta then the distorted + // sample is above the water surface. Mask it out + // by removing the distortion. + distortPos = IN.posPostWave; + delta = startDelta; + distortAmt = 0; + } + else + { + float diff = ( prepassDepth - startDepth ) * farPlaneDist; + + if ( diff < 0 ) + { + distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); + distortAmt *= saturate( delta / DISTORT_FULL_DEPTH ); + + distortDelta = bumpNorm.xy * distortAmt; + + distortPos = IN.posPostWave; + distortPos.xy += distortDelta; + + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + // Get prepass depth at the position of this distorted pixel. + prepassDepth = TORQUE_PREPASS_UNCONDITION( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; + delta = ( prepassDepth - pixelDepth ) * farPlaneDist; + } + + if ( delta < 0.1 ) + { + // If we got a negative delta then the distorted + // sample is above the water surface. Mask it out + // by removing the distortion. + distortPos = IN.posPostWave; + delta = startDelta; + distortAmt = 0; + } + } + + float4 temp = IN.posPreWave; + temp.xy += bumpNorm.xy * distortAmt; + float2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams1 ); + + float2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); + + float4 fakeColor = float4(ambientColor,1); + float3 eyeVec = IN.objPos.xyz - eyePos; + eyeVec = mul( (float3x3)modelMat, eyeVec ); + eyeVec = mul( IN.tangentMat, eyeVec ); + float3 reflectionVec = reflect( eyeVec, bumpNorm ); + + // Use fakeColor for ripple-normals that are angled towards the camera + eyeVec = -eyeVec; + eyeVec = normalize( eyeVec ); + float ang = saturate( dot( eyeVec, bumpNorm ) ); + float fakeColorAmt = ang; + + // for verts far from the reflect plane z position + float rplaneDist = abs( REFLECT_PLANE_Z - IN.objPos.w ); + rplaneDist = saturate( ( rplaneDist - 1.0 ) / 2.0 ); + rplaneDist *= ISRIVER; + fakeColorAmt = max( fakeColorAmt, rplaneDist ); + +#ifndef UNDERWATER + + // Get foam color and amount + float2 foamRippleOffset = bumpNorm.xy * FOAM_RIPPLE_INFLUENCE; + IN.foamTexCoords.xy += foamRippleOffset; + IN.foamTexCoords.zw += foamRippleOffset; + + float4 foamColor = TORQUE_TEX2D( foamMap, IN.foamTexCoords.xy ); + foamColor += TORQUE_TEX2D( foamMap, IN.foamTexCoords.zw ); + foamColor = saturate( foamColor ); + + // Modulate foam color by ambient color + // so we don't have glowing white foam at night. + foamColor.rgb = lerp( foamColor.rgb, ambientColor.rgb, FOAM_AMBIENT_LERP ); + + float foamDelta = saturate( delta / FOAM_MAX_DEPTH ); + float foamAmt = 1 - pow( foamDelta, 2 ); + + // Fade out the foam in very very low depth, + // this improves the shoreline a lot. + float diff = 0.8 - foamAmt; + if ( diff < 0.0 ) + foamAmt -= foamAmt * abs( diff ) / 0.2; + + foamAmt *= FOAM_OPACITY * foamColor.a; + + foamColor.rgb *= FOAM_OPACITY * foamAmt * foamColor.a; + + // Get reflection map color. + float4 refMapColor = TORQUE_TEX2D( reflectMap, reflectCoord ); + + // If we do not have a reflection texture then we use the cubemap. + refMapColor = lerp( refMapColor, TORQUE_TEXCUBE( skyMap, reflectionVec ), NO_REFLECT ); + + fakeColor = ( TORQUE_TEXCUBE( skyMap, reflectionVec ) ); + fakeColor.a = 1; + // Combine reflection color and fakeColor. + float4 reflectColor = lerp( refMapColor, fakeColor, fakeColorAmt ); + + // Get refract color + float4 refractColor = hdrDecode( TORQUE_TEX2D( refractBuff, refractCoord ) ); + + // We darken the refraction color a bit to make underwater + // elements look wet. We fade out this darkening near the + // surface in order to not have hard water edges. + // @param WET_DEPTH The depth in world units at which full darkening will be recieved. + // @param WET_COLOR_FACTOR The refract color is scaled down by this amount when at WET_DEPTH + refractColor.rgb *= 1.0f - ( saturate( delta / WET_DEPTH ) * WET_COLOR_FACTOR ); + + // Add Water fog/haze. + float fogDelta = delta - FOG_DENSITY_OFFSET; + + if ( fogDelta < 0.0 ) + fogDelta = 0.0; + float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * fogDelta ) ); + + // Calculate the water "base" color based on depth. + float4 waterBaseColor = baseColor * TORQUE_TEX1D( depthGradMap, saturate( delta / depthGradMax ) ); + waterBaseColor = toLinear(waterBaseColor); + + // Modulate baseColor by the ambientColor. + waterBaseColor *= float4( ambientColor.rgb, 1 ); + + // calc "diffuse" color by lerping from the water color + // to refraction image based on the water clarity. + float4 diffuseColor = lerp( refractColor, waterBaseColor, fogAmt ); + + // fresnel calculation + float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + + // Scale the frensel strength by fog amount + // so that parts that are very clear get very little reflection. + fresnelTerm *= fogAmt; + + // Also scale the frensel by our distance to the + // water surface. This removes the hard reflection + // when really close to the water surface. + fresnelTerm *= saturate( PIXEL_DIST - 0.1 ); + + fresnelTerm *= reflectivity; + + // Combine the diffuse color and reflection image via the + // fresnel term and set out output color. + float4 OUT = lerp( diffuseColor, reflectColor, fresnelTerm ); + + float3 lightVec = inLightVec; + + // Get some specular reflection. + float3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( newbump ); + float3 halfAng = normalize( eyeVec + -lightVec ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + // Scale down specularity in very shallow water to improve the transparency of the shoreline. + specular *= saturate( delta / 2 ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx ); + +#else + + float4 refractColor = hdrDecode( TORQUE_TEX2D( refractBuff, refractCoord ) ); + float4 OUT = refractColor; + +#endif + +#ifndef UNDERWATER + + OUT.rgb = OUT.rgb + foamColor.rgb; + + float factor = computeSceneFog( eyePos, + IN.objPos.xyz, + IN.objPos.w, + fogData.x, + fogData.y, + fogData.z ); + + OUT.rgb = lerp( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + //OUT.rgb = fogColor.rgb; + +#endif + + OUT.a = 1.0; + + //return OUT; + + return hdrEncode( OUT ); +} diff --git a/Templates/BaseGame/game/data/shaders/common/water/waterV.hlsl b/Templates/BaseGame/game/data/shaders/common/water/waterV.hlsl new file mode 100644 index 000000000..c869f0e9f --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/water/waterV.hlsl @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#include "../shaderModel.hlsl" + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + float3 position : POSITION; + float3 normal : NORMAL; + float2 undulateData : TEXCOORD0; + float4 horizonFactor : TEXCOORD1; +}; + +struct ConnectData +{ + float4 hpos : TORQUE_POSITION; + + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup + float4 rippleTexCoord01 : TEXCOORD0; + + // xy is TexCoord 2 for ripple texture lookup + // z is the Worldspace unit distance/depth of this vertex/pixel + // w is amount of the crestFoam ( more at crest of waves ). + float4 rippleTexCoord2 : TEXCOORD1; + + // Screenspace vert position BEFORE wave transformation + float4 posPreWave : TEXCOORD2; + + // Screenspace vert position AFTER wave transformation + float4 posPostWave : TEXCOORD3; + + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + float4 objPos : TEXCOORD4; + + float4 foamTexCoords : TEXCOORD5; + + float3x3 tangentMat : TEXCOORD6; +}; + +//----------------------------------------------------------------------------- +// Uniforms +//----------------------------------------------------------------------------- +uniform float4x4 modelMat; +uniform float4x4 modelview; +uniform float4 rippleMat[3]; +uniform float3 eyePos; +uniform float2 waveDir[3]; +uniform float2 waveData[3]; +uniform float2 rippleDir[3]; +uniform float2 rippleTexScale[3]; +uniform float3 rippleSpeed; +uniform float4 foamDir; +uniform float4 foamTexScale; +uniform float2 foamSpeed; +uniform float3 inLightVec; +uniform float gridElementSize; +uniform float elapsedTime; +uniform float undulateMaxDist; + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +ConnectData main( VertData IN ) +{ + ConnectData OUT; + + // use projection matrix for reflection / refraction texture coords + float4x4 texGen = { 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 }; + + IN.position.z = lerp( IN.position.z, eyePos.z, IN.horizonFactor.x ); + float4 inPos = float4( IN.position, 1.0); + OUT.objPos = inPos; + OUT.objPos.w = mul( modelMat, inPos ).z; + + // Send pre-undulation screenspace position + OUT.posPreWave = mul( modelview, inPos ); + OUT.posPreWave = mul( texGen, OUT.posPreWave ); + + // Calculate the undulation amount for this vertex. + float2 undulatePos = mul( modelMat, float4( IN.undulateData.xy, 0, 1 ) ).xy; + float undulateAmt = 0.0; + + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN.position.xyz, eyePos ); + dist = clamp( dist, 1.0, undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); + + // Also scale down wave magnitude if the camera is very very close. + undulateFade *= saturate( ( distance( IN.position.xyz, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + OUT.rippleTexCoord2.w = undulateAmt / ( waveData[0].y + waveData[1].y + waveData[2].y ); + OUT.rippleTexCoord2.w = saturate( OUT.rippleTexCoord2.w - 0.2 ) / 0.8; + + // Apply wave undulation to the vertex. + OUT.posPostWave = inPos; + OUT.posPostWave.xyz += IN.normal.xyz * undulateAmt; + + // Convert to screen + OUT.posPostWave = mul( modelview, OUT.posPostWave ); + + // Setup the OUT position symantic variable + OUT.hpos = OUT.posPostWave; + //OUT.hpos.z = lerp( OUT.hpos.z, OUT.hpos.w, IN.horizonFactor.x ); + + // if ( IN.horizonFactor.x > 0 ) + // { + // float3 awayVec = normalize( OUT.objPos.xyz - eyePos ); + // OUT.objPos.xy += awayVec.xy * 1000.0; + // } + + // Save world space camera dist/depth of the outgoing pixel + OUT.rippleTexCoord2.z = OUT.hpos.z; + + // Convert to reflection texture space + OUT.posPostWave = mul( texGen, OUT.posPostWave ); + + float2 txPos = undulatePos; + if ( IN.horizonFactor.x ) + txPos = normalize( txPos ) * 50000.0; + + // set up tex coordinates for the 3 interacting normal maps + OUT.rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT.rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + float2x2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[0][1] = rippleMat[0].y; + texMat[1][0] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT.rippleTexCoord01.xy = mul( texMat, OUT.rippleTexCoord01.xy ); + + OUT.rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT.rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[0][1] = rippleMat[1].y; + texMat[1][0] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT.rippleTexCoord01.zw = mul( texMat, OUT.rippleTexCoord01.zw ); + + OUT.rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT.rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + + texMat[0][0] = rippleMat[2].x; + texMat[0][1] = rippleMat[2].y; + texMat[1][0] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT.rippleTexCoord2.xy = mul( texMat, OUT.rippleTexCoord2.xy ); + + OUT.foamTexCoords.xy = txPos * foamTexScale.xy + foamDir.xy * foamSpeed.x * elapsedTime; + OUT.foamTexCoords.zw = txPos * foamTexScale.zw + foamDir.zw * foamSpeed.y * elapsedTime; + + + float3 binormal = float3( 1, 0, 0 ); + float3 tangent = float3( 0, 1, 0 ); + float3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + } + + binormal = binormal; + tangent = tangent; + normal = cross( binormal, tangent ); + + float3x3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + OUT.tangentMat = worldToTangent; + + return OUT; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/wavesP.hlsl b/Templates/BaseGame/game/data/shaders/common/wavesP.hlsl new file mode 100644 index 000000000..c51eb4b89 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/wavesP.hlsl @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "shdrConsts.h" +#include "shaderModel.hlsl" + +//----------------------------------------------------------------------------- +// Data +//----------------------------------------------------------------------------- +struct v2f +{ + float4 HPOS : TORQUE_POSITION; + float2 TEX0 : TEXCOORD0; + float4 tangentToCube0 : TEXCOORD1; + float4 tangentToCube1 : TEXCOORD2; + float4 tangentToCube2 : TEXCOORD3; + float4 lightVec : TEXCOORD4; + float3 pixPos : TEXCOORD5; + float3 eyePos : TEXCOORD6; +}; + + + +struct Fragout +{ + float4 col : TORQUE_TARGET0; +}; + +// Uniforms +TORQUE_UNIFORM_SAMPLER2D(diffMap,0); +//TORQUE_UNIFORM_SAMPLERCUBE(cubeMap, 1); not used? +TORQUE_UNIFORM_SAMPLER2D(bumpMap,2); + +uniform float4 specularColor : register(PC_MAT_SPECCOLOR); +uniform float4 ambient : register(PC_AMBIENT_COLOR); +uniform float specularPower : register(PC_MAT_SPECPOWER); +uniform float accumTime : register(PC_ACCUM_TIME); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Fragout main(v2f IN) +{ + Fragout OUT; + + float2 texOffset; + float sinOffset1 = sin( accumTime * 1.5 + IN.TEX0.y * 6.28319 * 3.0 ) * 0.03; + float sinOffset2 = sin( accumTime * 3.0 + IN.TEX0.y * 6.28319 ) * 0.04; + + texOffset.x = IN.TEX0.x + sinOffset1 + sinOffset2; + texOffset.y = IN.TEX0.y + cos( accumTime * 3.0 + IN.TEX0.x * 6.28319 * 2.0 ) * 0.05; + + + float4 bumpNorm = TORQUE_TEX2D( bumpMap, texOffset ) * 2.0 - 1.0; + float4 diffuse = TORQUE_TEX2D( diffMap, texOffset ); + + OUT.col = diffuse * (saturate( dot( IN.lightVec, bumpNorm.xyz ) ) + ambient); + + float3 eyeVec = normalize(IN.eyePos - IN.pixPos); + float3 halfAng = normalize(eyeVec + IN.lightVec.xyz); + float specular = saturate( dot(bumpNorm, halfAng) ) * IN.lightVec.w; + specular = pow(abs(specular), specularPower); + OUT.col += specularColor * specular; + + + + return OUT; +} + diff --git a/Templates/BaseGame/game/data/shaders/common/wavesV.hlsl b/Templates/BaseGame/game/data/shaders/common/wavesV.hlsl new file mode 100644 index 000000000..fccef9d25 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/wavesV.hlsl @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#define IN_HLSL +#include "shdrConsts.h" +#include "hlslStructs.hlsl" + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +struct Conn +{ + float4 HPOS : POSITION; + float2 TEX0 : TEXCOORD0; + float4 tangentToCube0 : TEXCOORD1; + float4 tangentToCube1 : TEXCOORD2; + float4 tangentToCube2 : TEXCOORD3; + float4 outLightVec : TEXCOORD4; + float3 pos : TEXCOORD5; + float3 outEyePos : TEXCOORD6; + +}; + + +uniform float4x4 modelview : register(VC_WORLD_PROJ); +uniform float3x3 cubeTrans : register(VC_CUBE_TRANS); +uniform float3 cubeEyePos : register(VC_CUBE_EYE_POS); +uniform float3 inLightVec : register(VC_LIGHT_DIR1); +uniform float3 eyePos : register(VC_EYE_POS); + +//----------------------------------------------------------------------------- +// Main +//----------------------------------------------------------------------------- +Conn main( VertexIn_PNTTTB In) +{ + Conn Out; + + Out.HPOS = mul(modelview, float4(In.pos,1.0)); + Out.TEX0 = In.uv0; + + + float3x3 objToTangentSpace; + objToTangentSpace[0] = In.T; + objToTangentSpace[1] = In.B; + objToTangentSpace[2] = In.normal; + + + Out.tangentToCube0.xyz = mul( objToTangentSpace, cubeTrans[0].xyz ); + Out.tangentToCube1.xyz = mul( objToTangentSpace, cubeTrans[1].xyz ); + Out.tangentToCube2.xyz = mul( objToTangentSpace, cubeTrans[2].xyz ); + + float3 pos = mul( cubeTrans, In.pos ).xyz; + float3 eye = cubeEyePos - pos; + normalize( eye ); + + Out.tangentToCube0.w = eye.x; + Out.tangentToCube1.w = eye.y; + Out.tangentToCube2.w = eye.z; + + Out.outLightVec.xyz = -inLightVec; + Out.outLightVec.xyz = mul(objToTangentSpace, Out.outLightVec); + Out.pos = mul(objToTangentSpace, In.pos.xyz / 100.0); + Out.outEyePos.xyz = mul(objToTangentSpace, eyePos.xyz / 100.0); + Out.outLightVec.w = step( 0.0, dot( -inLightVec, In.normal ) ); + + + return Out; +} + + diff --git a/Templates/BaseGame/game/data/shaders/common/wind.hlsl b/Templates/BaseGame/game/data/shaders/common/wind.hlsl new file mode 100644 index 000000000..b3fee7721 --- /dev/null +++ b/Templates/BaseGame/game/data/shaders/common/wind.hlsl @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// +// A tip of the hat.... +// +// The following wind effects were derived from the GPU Gems +// 3 chapter "Vegetation Procedural Animation and Shading in Crysis" +// by Tiago Sousa of Crytek. +// + +float4 smoothCurve( float4 x ) +{ + return x * x * ( 3.0 - 2.0 * x ); +} + +float4 triangleWave( float4 x ) +{ + return abs( frac( x + 0.5 ) * 2.0 - 1.0 ); +} + +float4 smoothTriangleWave( float4 x ) +{ + return smoothCurve( triangleWave( x ) ); +} + +float3 windTrunkBending( float3 vPos, float2 vWind, float fBendFactor ) +{ + // Smooth the bending factor and increase + // the near by height limit. + fBendFactor += 1.0; + fBendFactor *= fBendFactor; + fBendFactor = fBendFactor * fBendFactor - fBendFactor; + + // Displace the vert. + float3 vNewPos = vPos; + vNewPos.xy += vWind * fBendFactor; + + // Limit the length which makes the bend more + // spherical and prevents stretching. + float fLength = length( vPos ); + vPos = normalize( vNewPos ) * fLength; + + return vPos; +} + +float3 windBranchBending( float3 vPos, + float3 vNormal, + + float fTime, + float fWindSpeed, + + float fBranchPhase, + float fBranchAmp, + float fBranchAtten, + + float fDetailPhase, + float fDetailAmp, + float fDetailFreq, + + float fEdgeAtten ) +{ + float fVertPhase = dot( vPos, fDetailPhase + fBranchPhase ); + + float2 vWavesIn = fTime + float2( fVertPhase, fBranchPhase ); + + float4 vWaves = ( frac( vWavesIn.xxyy * + float4( 1.975, 0.793, 0.375, 0.193 ) ) * + 2.0 - 1.0 ) * fWindSpeed * fDetailFreq; + + vWaves = smoothTriangleWave( vWaves ); + + float2 vWavesSum = vWaves.xz + vWaves.yw; + + // We want the branches to bend both up and down. + vWavesSum.y = 1 - ( vWavesSum.y * 2 ); + + vPos += vWavesSum.xxy * float3( fEdgeAtten * fDetailAmp * vNormal.xy, + fBranchAtten * fBranchAmp ); + + return vPos; +} diff --git a/Templates/BaseGame/game/data/splash.png b/Templates/BaseGame/game/data/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..333df9eb3950726e7eb4ebde5dfe034e8a0b1a6f GIT binary patch literal 11864 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/torque.png b/Templates/BaseGame/game/data/torque.png new file mode 100644 index 0000000000000000000000000000000000000000..1023f2bb6655b4e5740ee9dbd56a2e4c2ac27594 GIT binary patch literal 1331 zcmV-31ue_)p4!cG4G5pgl# zAhXdy5XFU{3qgyx>Ozo$8!ePkTLhV=SS?N4Nt>6Ky!+m9(I!or`(9uAUPwI4ymxQT zcfNDJbMAdWkL;b%3Bv!?KFt^C@ufMsl1lX)gckOOKTSPN1#!IXs3v+IEFRWI4{}qq zR+w!%4*>I;j=3JQ5DEj|OGECN;xQuH8j|N4BzTMwrlDY$+F&27b3nZhz)hu)iVj|>P2n1l;sIZ#d z-I`d)ECd4ZFgHyf50Bz*EY;Pz5zg@-H${Cb;`c`<>5X`z*@drbF>FLEtXQV6^%=o& zO^=4s+`3wPZ)}oY)nm#I3;-XQWvt4Yuf48EVpz88Om zGXEL5VwsrW!3+9a^xschrt=(MwyGWTk6-)ClGq5)+*}#>B441J(YWiv64_X)8(5XK z3pw7ZN~AU0b>dPoNuj3D2p3qY8(5JhZtDpYc>8Nx3Qz~OgRCFFN(VXkU!(Uo7_yebuAS1RCaEio_HWY9nkb2+g&S7qfZt8vHy*iKjG8*#lGs z90&l{5aY+O{48G}0U##?p3_d5*&CgpcXzj0R{)fEJ_-gjA1Qpl^e=lmIjoH2tw)GWTpPX=&2BuOE&Hz0D;5Kx59b&39ASvW^>S;6;m5Dwx(3G4_TjB@P=$QUA pF#IVk{A1f~bTu@*AIj5e{{eIA^)eNAYt;Y%002ovPDHLkV1ikdVpISC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/UI.cs b/Templates/BaseGame/game/data/ui/UI.cs new file mode 100644 index 000000000..323fda930 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/UI.cs @@ -0,0 +1,63 @@ + +// The general flow of a gane - server's creation, loading and hosting clients, and then destruction is as follows: + +// First, a client will always create a server in the event that they want to host a single player +// game. Torque3D treats even single player connections as a soft multiplayer game, with some stuff +// in the networking short-circuited to sidestep around lag and packet transmission times. + +// initServer() is called, loading the default server scripts. +// After that, if this is a dedicated server session, initDedicated() is called, otherwise initClient is called +// to prep a playable client session. + +// When a local game is started - a listen server - via calling StartGame() a server is created and then the client is +// connected to it via createAndConnectToLocalServer(). + +function UI::create( %this ) +{ + if ($Server::Dedicated) + return; + + // Use our prefs to configure our Canvas/Window + configureCanvas(); + + //Load UI stuff + //we need to load this because some of the menu profiles use the sounds here + exec("./scripts/datablocks/guiSounds.cs"); + + //Profiles + exec("./scripts/profiles.cs"); + + //Now gui files + exec("./scripts/guis/mainMenu.gui"); + exec("./scripts/guis/chooseLevelDlg.gui"); + exec("./scripts/guis/joinServerMenu.gui"); + exec("./scripts/guis/loadingGui.gui"); + exec("./scripts/guis/optionsMenu.gui"); + exec("./scripts/guis/pauseMenu.gui"); + exec("./scripts/guis/remapDlg.gui"); + exec("./scripts/guis/remapConfirmDlg.gui"); + + exec("./scripts/guis/profiler.gui"); + exec("./scripts/guis/netGraphGui.gui"); + + //Load gui companion scripts + exec("./scripts/chooseLevelDlg.cs"); + exec("./scripts/optionsList.cs"); + exec("./scripts/optionsMenu.cs"); + exec("./scripts/graphicsMenu.cs"); + exec("./scripts/controlsMenu.cs"); + exec("./scripts/chooseLevelDlg.cs"); + exec("./scripts/mainMenu.cs"); + exec("./scripts/joinServerMenu.cs"); + exec("./scripts/pauseMenu.cs"); + exec("./scripts/messageBoxes.cs"); + + %dbList = new ArrayObject(LevelFilesList); + + Canvas.pushDialog(MainMenuGui); +} + +function Game::destroy( %this ) +{ + +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/UI.module b/Templates/BaseGame/game/data/ui/UI.module new file mode 100644 index 000000000..e22117d39 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/UI.module @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/art/ScreenBrightness_Dark.png b/Templates/BaseGame/game/data/ui/art/ScreenBrightness_Dark.png new file mode 100644 index 0000000000000000000000000000000000000000..85cde0bbc35db7c8315d905eed52102ce80a1e91 GIT binary patch literal 18962 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

az=4G{&Sm3_c`hMIiZC>mrsYf>cZr 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 IQpVr@2Vh;nHvj+t literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/Torque-3D-logo-w.png b/Templates/BaseGame/game/data/ui/art/Torque-3D-logo-w.png new file mode 100644 index 0000000000000000000000000000000000000000..ec197dda39246e2f1f0a95f79d35a68aba584a01 GIT binary patch literal 19328 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/Torque-3D-logo.png b/Templates/BaseGame/game/data/ui/art/Torque-3D-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e31d42a688a7e560b17237ff60edbf9c0c00dc4d GIT binary patch literal 9063 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/Torque-3D-logo_alt.png b/Templates/BaseGame/game/data/ui/art/Torque-3D-logo_alt.png new file mode 100644 index 0000000000000000000000000000000000000000..3836f1e7fc26b23ee299fa02570bbed343d2cb50 GIT binary patch literal 11616 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/background-dark.png b/Templates/BaseGame/game/data/ui/art/background-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fedbcfc93cc49adb736f9e8bb8f04cfb386ae0ca GIT binary patch literal 2350 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i0*Z)=h^hlA&H|6fVg?3oVGw3ym^DWN zD9B#o>FdgVpG! zm=mFHqLL!0?NEm!xe3`iBt=Le0u&{z2q{EBTCtl%sv=ON5i|)&5h!Mn9FAlic11{G zi#t(}st74;L0XaAgk%zuBIK|ICRDi0*Z)=h^hlAmUKs7M+S!VC(K#9UIO`& zC9V-A!TD(=<%vb942~)JNvR5+xryniL8*x;m4zo$Z5S9hO*~y3Ln>~)xogOG$Uwy9 zVE&fx`*X8itemj5W5OAkgI6yfn^7uroPpuO=K>a>h1?7d35*OJIzZdR7#a>RF$hF3 zfVqvV3?Ke)n#0K8V7|dEk&z+frwa!|z%)Y;Gjanqw#qOu zI80`RTiy_Tvd0^!)mIGY5TMOay$rt}^;t7GOh+gLNy1zSvLyFRChvm9+r^abJ{rx7@#M$iBJo2p#JGIMZh%UiWW|o(YT|b;Wsape}D%-Q_ODfGvEH0udG`(4_ESf6BD| z)octIo1p0o7=s1cGYi>)wwy%Ra$xNdV_>Ys67(2S`h=wtRNo(vOt}UNqlQg4)|2R7 zERh6CL(oJ?RQkrQ2xc%|laLg_f)wgIr0hh2BA7p*856rnNQz*Y85#sgZo;7mDaWIw zEYw&c$t0u*0Gf~NCM4^S6d^?bNR*%=q_9OxUPva9st74;L0Yk!gro>5L_nfQ)?rtK z6agTuL?|jERN$kzlO|z@O_)U?EBosbPnlP)GeSD025?m{EpRI zFmZbu(+PLhoG1hv0MaZaCFLM1D+^a>Zf=g%HjwzgfBzUBJ$h7-9Afd2VJR>SC=EoG z4eQsV*eEF_#qj*uGYp03;wMj@WC#chWME>T5R4iCNeAG#MF!}6eBxjO{{H=oB8bk% zCk`>-&mUBUXncI)U<3Z3N&LZKz#rTO{6$1;mYqr^NsNQG)@Uk1j4GBnYAusFD(yKmqgZ%V#(S0c1Wd zaae$oaE z3l)_)2$eJ1NUIy!|GX0DZ>~esOhEkQ<14J*LK2_Az#v=*tJ?P4+1WX`Kq?D@t#wc{ zXUC2m45v<=Y6Mny_0&m;Ad_Jj1d#c-#D`Hz95rCnfI(C*4DH+qE>!F4>QMRx$b3TL tqy2@^{=#T~Vbp-pdSSG`fD8Zv3;=)Qn}Ko{3G@H}002ovPDHLkV1nEDBVqsm literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/chatHudBorderArray.png b/Templates/BaseGame/game/data/ui/art/chatHudBorderArray.png new file mode 100644 index 0000000000000000000000000000000000000000..1aebdb2d8bb91a1fb5687d2572597bf9f5b21753 GIT binary patch literal 1176 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 S3QYOT0XMIk>c74QkN6*F=2BAt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/clear-btn_d.png b/Templates/BaseGame/game/data/ui/art/clear-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..229c71e8bddaa102f495a3dba47004f9c65cd0d4 GIT binary patch literal 593 zcmV-X00s?QkPn?)w z6CVEP+t+W**N+`j{PO<2JW!f(R$hMapFe*XQ8a;^}fcc&U1q&WrzGUsRJ~|Nb$;HG%v9vf=yp@5~EJ%j{0ATbF1a6f`foz5TGU zkMHBZK=}_JKCu4%`b@n56}$ZWa=w55ESxrV$`TGP&c7%D0g8vBl`A{>#l#-3tgkQ2 zR8m^7wz;{KM@Z;(+3GdzvKks+{{Hz34m}oFOoJj$N=5Z+-m>NMzkdJ5{O|8yMqVD? zKm39MzuDN>{(%B$^X5%VKwbYCk<;G)|Nj|*als698ry$h&@!{I{AFfl{tFTZ1~u4K f&!0Y90T5sS_@w(H5qG>u00000NkvXXu0mjf2Ztbp literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/clear-btn_h.png b/Templates/BaseGame/game/data/ui/art/clear-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5e67cb13b2b4a85172bfa47e74b66993f0b315a2 GIT binary patch literal 595 zcmV-Z0<8UsP)Z)GIKgXfZeFjXq_kgNUj7V-2Ju1S$m*D2rhp`p zl9Fcr{{5R5=;ZW=4s#>mglFXG_fcy{jGxe}m8#*ZI9GM_(p zPV4K}uWDReT(?V0OVju5+t&+J`X6r7>({RxLPA1j0TnU5ee;GTDJjwa)albT#>U3& zL4koCPoF;ZdGX=}>z_Y=nBbcJ{{71fH0}21PoJ2xva|ipoIP7*Vq(%dVZwyX5fPEM zf$}_W-n;>6Vum}9nVI=>M@NUoh4be%&z?P7X=ZBLvS8uDP2azNXPP-ylRQAAkeFdrZPpa1{=Gs2xW zapJ^25Y5TSd0R(E=N8C@o}O;)$f)S{??1kax3#zR@eA<(K?!mT3yaSa`unr^czN$E zUc7iJFaS<0UAAlz7dO|l>C>iXT3T9u1p0>&9^{~y21T5Oh2^KYbLX{x`t-3I=nMva zet|!dQj))bw*LbK(3UM*m>C%v|1%<|J&=2U{P@8N#BBfm{$pZgW%wAA<=LJelEZTKubwTGJIwqVjwiI($lj%e)b#h zSV}4U^((?ZzW5oeF0X{QG;0x_gI(Zp1%{y0u%eoqB*Be}+Z35ISogfn=j{1}M~e5r zom^~6`il#Z>bB`G@&P}3s^gaM{dn?`64>vd+uy zC)Ag|rg#ir`9=|JM=PFw+g^xf&*YE1hYH5v6--s@cbdf?FJvm;W#*#H1Rw9hgS0Vv4;mgMH;x)r;fhYuV;(S;&;;NSt=u63|? zz%6O(;J_d!D}&pg-rnB0C5=o>QFLPjLReThZb@}b%@L7I2uePB_z*;W0B5A7%a^0e egYzdqfB^t3O)Y)0 z9XimShOxs=YQDsp@gJb1gn_Zvu?5E&#SyLef*bddTyF3YCK)zo&)JhrF5V0V0e+4h z&!GoIxa##XdV>0jSi8!D*$CI(frpPId zoX)3*M|j)3>wnqT_NAKB=~QbCw1-x!H26BvM$lT86^U`fnQd^FLPPQqy@lE%w=B^f)Fy&H|PPw->A71`mp00RIT5=iSlljg+$0000< KMNUMnLSTYWrq$s9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/collapse-toolbar_n.png b/Templates/BaseGame/game/data/ui/art/collapse-toolbar_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b36de3ae049d9743a09dc6fc26af740cf9841360 GIT binary patch literal 439 zcmV;o0Z9IdP)Ox_mJPyDg4SlUjSNW3W5uGwOEk!Lx4uX4pZl7UqAx0i=rnvSAdP_$$A20Su(H_ zvh10InGB}9^1v-*3Cso@F>PaL>L}8isTm_PeHKN5r}hODW+z|w`}Q5O#yHtL&*`VC zP~)7=kRBzVBuOw+YZy|xe)*kcR2>cgwbuTO8@Fkiwr+iIX4$!2->q9vYwhDG`qk=o zMb~kBVd5z4oUzQ=+;7}ZXk^2MX<21r!6szg-dfzih^e4%lbv7^R`VYwdnU3IbzN)k2xeK9 hJ@8zg^|AXCU;u#ob3iiM+rIz+002ovPDHLkV1m|A!NdRn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/defaultCursor.png b/Templates/BaseGame/game/data/ui/art/defaultCursor.png new file mode 100644 index 0000000000000000000000000000000000000000..ee03cd483c6b7182fd8cb74d8151175c5cfba6a7 GIT binary patch literal 615 zcmW+!Ye&8vB;<99 zZEMwt5VC$~D6E?>$`C2gF|i+Wib-f#KL*{T8(p9u-9G(s&I8YR&T}}N6LeP|kTY^J z06=y*9aSnKHLxhCc~kILQU%^;t*`I}=yfN^!t2QrCN_K*X zg=h&0T^J0J)H0ny5sv}gV`G3GGUfu+R6?xc!=E9&52xbFL<>&!3V3U|g@Bo4KOy(E zKX0zT-W-^UoFX6DM4Tk0c?ppsZgHTK)r5DW?@Y|l=NZooSO#8uwI4JhK4FiP8S70) z@^{#gzEfSf1;*>JZyVcc;*5W-{e^T< zdaK?e?gMT?a9^tdr7=2#XR@TZ242KAVM40A_|hZ}FL_g15i7a^y(K?337DGp1LH8W qA`a;Y90~&)P*XNP)ppmN?KzNCm_JkRo4DK%mf8 zqQQP30$^=Dp|L`DTWhec!&7&H>n8ZNw0bMDDUS#x#h5 z$D^abV#)=)U#6@u4Y5Aok{5|YmNzyw&J|%RQ6Riuh9C~&QJ+i=IOE>lUO3I1APU@J zfy~LVGF09#RU}CQ#EHk_K{=+A4Vt5dBI@6u`W1w>$gk9fQ~Fn9@uSviw~LmAU!rr( zXI>)s@$oVIol24IZfT)5kxWu+Hk;v|!=Vj$CX<2njZHW>I3T~fyPMjbogHc&47l%` zufcA&S03DGG(ty*4>mV9;ppfH4i670j&@U169fVQuvjeD-me_BY%WKtwnaX#4#zzh z92^9v(@CJHXk)Csy`46zx-TJkE}J7&+ajOWPZ}D)<#NHu$OyRIZc?-{hQINB370*a z%Yw)j`69vP9UB`{o@Vdg|5N;KSC5PzrD)W=yvv?~PM2TETrPLJqgL+BZ*$!ha7og^ z+1XjX6Kv){qc1g91xW}ONn`m(l88%Y+=G!iNvG3~$Kz=RzAOLFtyTW0XRTMG>eZ-^6a4o-{`99(sbHQqd7okX z>ec=abW-jZ_r84aU_~?V&)YxSOC%D%u(uum%a_~1{QG>?<=eBN@*=bZ^_*L{uN*VCO$Ad Tw}rU>00000NkvXXu0mjfi+6Y@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/dropdown-button-arrow.png b/Templates/BaseGame/game/data/ui/art/dropdown-button-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c420ab85dad8b5ee3ca92464665ae071a471732 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%sd>6MhE&{2%E`$|abUXZ<1r^pL9db1gt1VxgHed#<;qq~ cW)1^}6XL9#0Z$iS1nOY$boFyt=akR{0H4Any8r+H literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/dropdown-textEdit.png b/Templates/BaseGame/game/data/ui/art/dropdown-textEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..3966efbb56239be59ee13cddceab917b144882b9 GIT binary patch literal 390 zcmV;10eSw3P)^?7l2ZX@QwNIJ_tdJyVq-O;hN*R;|Jf8oKt&?reAz#jswl zv@I};de2`VaYRw1b@G>k2y~N}rTzTSqF{8F^S*pfDsBZxV07vonVb4jntpET307*qoM6N<$f}R+y&Hw-a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/dropslider_d.png b/Templates/BaseGame/game/data/ui/art/dropslider_d.png new file mode 100644 index 0000000000000000000000000000000000000000..0c65347aa49bce5bb7ac3175bb4e7254a2e5c439 GIT binary patch literal 433 zcmV;i0Z#sjP)qL11TN-0d!L<;JkL6p$?;pB>P1`$fQ+U<6W zX0!P(ljI{R`f|C#d>$ZMNjWu96hYGc@pufz z7+wYg$a1$jH9-(c-f6dC7)EwJ8jbKan_;zH|Iu6ok{S|pU5BD5nPxhjVz>L^f><6@ zwEg}Ywr!_>EJCl>%RrB<7FSydk|YtE%?7Hfo_;W%I_<1h)ivU{BudaU4Ph8U?mpu# zg%mZVh`@=65&@IRE0W_8NmAY0tg-{r%gNzzQMa&sYITkKYK{AcYsW}7lQNKHSvL>1 b9{~mc?Gaq0{#;i100000NkvXXu0mjfD>J)D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/dropslider_h.png b/Templates/BaseGame/game/data/ui/art/dropslider_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2cb89d828f36ef133b7b15f0096785c10e4a72 GIT binary patch literal 431 zcmV;g0Z{&lP)yACobIGv~e}4R%gkcz{s)~YW%wFOt&reffYgKASQPhfo zMwC->wO-@Vd&2A6ii+1?iLJ~0nPC`~{_(I_V7Yvu_8f!I{2qhjIIt`W$8kUi!DKSQ z-R%u^UY&~i*L{peqYAfe8@})3eY2q|>LlWM4#VLPq9|59K@ecKOS-M?4~K@_B+X!1 zRy8YGmNA>nAP54r_nYXjZ-Q4mk2sE@C<^BDIeNVwn3mp}qJ0D83!=eb08P^>n^~5% zew>|((j>+Gbc*eEi=>!MS9B^WYe|~?>32C1Q55}pnUE%tBuVG4k!mMZM22Bpyx6`3 Z7yw+%?*7zSP`UsB002ovPDHLkV1hVF#EJj_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/dropslider_n.png b/Templates/BaseGame/game/data/ui/art/dropslider_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a4577f9eaa14cd8f70164c0e7f6ea0a61e0ffe69 GIT binary patch literal 428 zcmV;d0aN~oP)lfAU!yF`DqdWaWf_CP*G0#15QO2cn2Mq-t161)1cqV2_kD~; zBWRk2Goq+2b_bMJ4!IMKz+wo3fZ1$@(|(_P!ur^6P?~1dVsjisQG}|hq|gkKBx&wi zTJs9x2-E2ef*?Q`h3(p3r>NMAqPW%1-dm$4V;Hsy62hgS0Vv4;mgMHsH*7Al-)#9Kh|`0|)U)I@mj4 zmz0y0VX$?;A?fYyjom>YT}CFR*d@cl!f^+Jx~ArcNG3!}K6>~NM125fq@~N3qsxQy cCqRG!07OkOz~-BXZ2$lO07*qoM6N<$f=)$pfdBvi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/expand-toolbar_h.png b/Templates/BaseGame/game/data/ui/art/expand-toolbar_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c33bcad6985b3862324279bc0f5457755b94f28f GIT binary patch literal 468 zcmV;_0W1EAP)qS?llb{VTLkjAsY;|aeyj=$>HXDkKfU1h$Gh(W#P&4TdW&oR`Nq&a3 zTN8um?Q=toI@keY`=EJd?T*CDZYvdgmA#wIqEGOnEJb$uEx-Uri&4ja71Rp=0000< KMNUMnLSTZ?xyoSx literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/expand-toolbar_n.png b/Templates/BaseGame/game/data/ui/art/expand-toolbar_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0af2f1bd13809d099ca3a5a5c9ba1dc42b6f66c5 GIT binary patch literal 437 zcmV;m0ZRUfP)4~o#5^Dg=ri|$!*BY zbpP3B?F$Y zwO%=>X`z&@9drv@g0dk;N^TmO`W3-u+KiE!MNwe&sko4$lziUp$nSm5RBnu=@2aBx z{tJ6eXN!gmYNct4`g&Fzw)&+;bvRI-=QzXGHr@KjvWz??HC?wpRyQl0+1YZdyi5{W zUN4=}-z_)f`gSe%?Dp<%8~-FuXmPb@UGQXjFw*+wZiCf_ZQoNiaUAu!c4K0>t=SF5 z!{LASY^7JJHJ)q5(e1RYDluRawr*}2FECOnsnf(0O2YO1qqJU$>_lDHiXEXW%d$tF f>$5J~UjYUHlxB7wIAEv(00000NkvXXu0mjf?KH=8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/folder.png b/Templates/BaseGame/game/data/ui/art/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..571a904d00bf3c469eb0b5d146666f3e694c0ebe GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X`aE46Ln>}1{rUgjo>{fw=^d`}7_}IKzB{QNCm4=xK7IG! z>mO-J9(OEvbUi%2d^>l;nbwL}2Bm-h{&gxUCOp$nejbt)>aDV>%C+tc`xc6(AJm7C-`}5m*BR88Bk4As1J+mzLe7U=- iw;9~LHXA!MFfx2lbli6~+`Rq)0SqBM&U{ zK~RE8k;)fTv@rNVFsUphN-&N10jmTOk|LETicKV_m54TpVAK!`|L<7|`4E(7K4ARB zDus5>J(HaE?!4W7v&qH}czMI@+&44#&Ye5=o}G2_O1ZzkpGs6d@8oj17WcW!E!8%I zG7SwXm(AMBn08BdCX>0T948-RoKK$0X0t6*Q&aiH#l^fexw~LXm>2UyPMmS3yc8^% zPN&txvs=#V2iKgriKmXs^9tJFqjWWIBOj#Opr!-61IK}Xx1-s?{L4G0_&?_ilXv4x zqfZ_btu8MwSK|-wsQY~v)cnk(&+H?8J(|C{sz$fn!M^&){#~l$>@n5f-deC_;MX2J zsxZHFtA)8SUGF$_vOr$N_B~qGz~m2~oD@4WwsorNs!FYtyCThVSEZ(BX4T=g^Lm1w z!FRq%(Y|`AS3jc65Sso~f)<#1w-6X-QZF0;z1&IJw>OAO*=w$5nIM949%2g^Yzljy(oWy)Hf= zKP~7s_`nZIkS{YqF~l(je$;4#Z=xBAo(3O1_wqgCBii5tJte9r;~AA?zGq4`(Ts_n zWQL)#a^A!gm6r6qM+nP?0Sc)kNm@4Xtw-=SGYrxowl6GaQ(5@5B&1Y;X$f*x_i`5k zxlG?JMp&eYe3Cfrf#}H=#@X;~|BtO`5XzBM3_XQWv3X-F3LkUd8np$mW$;ZD@}tlb z@grOWzKMePX$!hz`6y0z95fS#p0K!L8Eqx>`_a=%75$H%65feFik{-gOB_87-@a&t zo=kbNraT|vFoAMIeOeI8_0#*bHBgfWlms3M{F=|;vk&HAk-V?7qf70j+dSQteXuCB zxme0WXRY7zZQiQU6W{Gki*a6ad*$6z80sP_3%q+`TtsI83dY1J6eRh$8vuYoz-JP{ zd_mbs-aVmUVdndNBq%XE#kqTuz$8gP5_U;MoS^NwxEOTzWZpMPmn%SJ z?(zSC4+0292nh@kvId`u$uPqZCPNED8bgi@LmbC`Ql1P|ln1?_pHA~@{Z%BXuPKfW j%4_?bDUt6}KLr>7B{DrC{G$C100000NkvXXu0mjfaJE^D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/hudfill.png b/Templates/BaseGame/game/data/ui/art/hudfill.png new file mode 100644 index 0000000000000000000000000000000000000000..57d72468ba801d41746de99b18edf1616d89f804 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X6g*uVLn>}1r6eTWIOD*WkhDOcN6H}UzzW9ZqYMoU%nXtX W87_0|zOoIdmci52&t;ucLK6V=Y$V12 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/inactive-overlay.png b/Templates/BaseGame/game/data/ui/art/inactive-overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..feab83209cc442c5ad8dc73c2b86e0a8115c4743 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!foRZkbkkcwMLfBw9D?8~Obq{gPz9LVg>D`Q%4bP0l+XkKL$W0) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/lagIcon.png b/Templates/BaseGame/game/data/ui/art/lagIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..cf158dd659ec28137fb146a0b86cf6ee5acc079f GIT binary patch literal 2941 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 T^O^6(Lx7(4eb^sb$cX;}dvI|) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/loadingbar.png b/Templates/BaseGame/game/data/ui/art/loadingbar.png new file mode 100644 index 0000000000000000000000000000000000000000..34f5944030dce5a174233bcef11b7d7210bbeac8 GIT binary patch literal 635 zcmV->0)+jEP)X1^@s6jP_Wj0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!9Z5t%RCwC#SUXO`KoDI!c0P`hC>MZ^ z3Q^J_B?TOUQy@A{K+7q(h9e;wN-Csu93WES#QFHMyfI$OxSov|DWj}**K*#opXal? zj#di4lQqp#T07jJ)9LgI-80%vIGYWolS#yh-@trBdkVwwlUEoErnP?U_xl%}PRFPD zcsw3(a~RD1et)~!Y|b&f{P)7-d4A@)uAd}HF*iM@8BF3KKK@X$BaAv64%NIq=CV2_ z@re)yp>$MXh);wtS(d3dj+O8G)>N=P6&&Q9VnT^Lh8~!jc?oW-i!CPk(u3B&qKz+F$2m8+DN)hk)#Nd*IpvAAWTx@%cTgDpt)vqhvdjE460sYwgX~>fgp+Jd2YAc)uk|W zMqswUx*Y4qwd(K9>fgA(xEI(Yw%+Y_pmXGSOt05-;O@U9p2zMnz$^}Az(Zc6MF>Oj z4_B|gJW49NFb0#(e6VKHQbH+(Fn{uajT~dfHV(q9Ri%b7sRdyw% zzi!h|6vy!oZq?QZ5FiAV3JXIbdEf>50EiV>fGO|5OC&=!78Ef>8Bq3Qi85u&128p; zy0IZuNc_8(!8W*1U@^{;pLAtkAIs!2K9w$g(Vi5E_R_(TXt!Yy=`jy9C$-#BTsiLtF^ZB*Y&9 znu7Q%Kob!E1gJtR7K_Jnv#QNCQ?x?}=aN;E{wiE|-pcC($g(Vq$KxLmyGVyf(^zDGu`zoK6+O&r|$k39P_#Td{RVb=+RJ>oS z5_ubL;HvzHT|;{^nY=DrGn>s`;clG()pMK_`&g{q0_X_0i)Zg~iVyNO)CzD>Y#&$T z6AvG8hEE01e+4MxJAN9@w_??je5G09;K{D}eS*M%1K|)3;Sdht5DwuGZXkLN#U?&;2#0VVG~OWLqO$_xwj$iwUn0Y= zTo@|cnW~4X1iKaC5DwuG?lQtfZBR<1p~0~bLI418&f&Hq+?9tLw%ELV`}VI%l29on zP1BTSSw_<|WkLwj<{1Eh_dWms5JEObj`u$N_Wb!zKR%T^-e-3fMZukOzG)h7jPb1NNGWA3rHuQ8lTyZYT{~lp z*IGL!gdjqQ4d4LaY*kfO2tnA*n>R<(=~Paq(`+)CniuTH9;othL@4;9tz6SIzWu=&V{D&r z#u#g@bsf{N#pdJ3kB=vlNpj=Hjr98U>+=5n`+xQI;(zt()%PbSC+gk1chzh*($6gvbH*5>9TQ=UMR6Px&N*d_^?Ko9j1kT`ZHpBVN-1HC5lShE3Z+vyU0S|rk4fG$Fn?B%zj zDnV6p9y@gzRLN%{`%)!4RS#7OsuENss7lTu`cNg`=B$LhefxG*mSwYAt?K!FUiAs~ z=FOXb=ks|ro6Ype$%$rlUHfj_E`(_Mgi}g2N-0~{wX5seGp+T;2M|Kg0l+n#dBUoy zvYc}g$1$Olnm*x{%cUvH(ki8FRaKUiW$7rTP1hxdhlh9e@*y5RgyZAm?{>~_t+7}v zYOS>`%hIe?t7f@en&on7*6VdsmZf7=RXIv&6m6X)&N*+4@kx@HJ{I`eezvyyK#mVMXm~8GG=(nCVF85^f-o1MVeZBZ! zzkdBUPBw9}`OQx@cYSYP_++yK+Z7O1396FwQnrIuCA&cOqDpqH9;y;lC8$bJm7GJM z-+ErEkFZb~wegyd8#fukz?wa3T1N~O?TL&jL0Y9dK za0rKR2#0V8hj0jYnc+}uUW#pv_M8@7C|uZcT7=y?bhr!uo^Z71-(BeUgabZvR~Zh! vCw$@6Lsf#^if{;ra0rKR2#0V8_toM*na3TX4k5xX00000NkvXXu0mjfnS#?O literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/menu.png b/Templates/BaseGame/game/data/ui/art/menu.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7a33f587e7c9ba19468b7a53295b0cd280104d GIT binary patch literal 1362 zcmV-Y1+DstP)1^@s6Ed2v;0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$^hrcPRCwC#Tgz_LFch`JbiqeJvw;O{ zx~_Pr5)#YuAxMZVjXE<``W^fNuw+w-NBM(Uz$PPh`~pHI_KY*v&b@xbX_{%rYBWh4 zyFTYWeJ8^OWA)$dZ4)L$j&*R(E5!`aU$?4$i0q%uX4PyyA3XsX1>Q5x*)PFwiw0yi zSOmQD+6KE#;2RClpdol+s&<8d&u!2Ue0`p84FMkT%mp5k{I-Bs1lV8^@Xl)+>^6aK zG(dwEw8zgW$wScF23~3YE)O8{fb+^Ujgx%cQ8{x1OmiG#kSMYlGfXRM%@_Onkk|G- zeP5j`OA&TIc5lDASC5%ar$)cLw(sft&HowTU=T0{yhXx>0AYZn51|Rr;4t7V5(Xk5 zgDT&sd3dWR$LPuu$K)(Cm)%E@z+y=l?OuQ)VXYyHND}1zMGOgR4O#5M;Lu@^40~-= zfFZ5&WR0ycWZ1*T4ACg>kA*`J1@;|bPhe4i^8Q#j95M>9pwI^i!Set-{_BS10aW~N z2pKvI(i(D-cZd#oY;=&Oa2?UXOTbMJjhRcr4)D1r^hHVdI`e{C9|kiOp6Y|I3_eG% z%|7Ef;~G3Sn@)}AjO(#ig>RPz>9fnfchW{sS}R9Sc>ch zgLCkp=f?g;Nrp*X_&s&*-27NlA9J-BErcwu*jgY-@DKsF#8rS6RAVG!LP=cL z15_o*ZTGF(00z_`VoVyKi=hDp(P|CWXZO`Q?;ow*?-C)~1Y9)$r5U^k*%js?J3*xx z^Z96e0hd)61FXBISB2F646xT(@7ryM15AJu9U^4gT1zr@yxJxQq|!uNg1I^gQokQy z9FZw;^7x~pBjXq{dCFX%c!I5_j+aFENO=F|1)IKlnC1gkYgLPd3$M>gMnwqQ1X9LJ z6OdppE-o72`rKz4$1k5O5AOa^=kt%hL=pjQ^E{V}ZQV0K00O#>*96#2l3?H0#Ap(J zFTO1AKYq?mj~{d<0KO$=9JFemps91}cnJ<3A*ONf(X(C)A}vP-f!R`Qf0vjx9&?0{ z7(@$tagr0bP_vZ{Fep76NMGYAsY5bsVtOUOw&RVYIO^ej_hQFW_KpG7&(=PmukA2q zvn9D$%>Glx`)rKdC6r8JiYq)OjQfbFQFlZ?diK1Zh{zuOI_Y5-!xb`DSO>{iclrrsdwFo&))aK6Om z2!5X^p~kQ-h_nZ5)5UyC0|pS#X%35k*yP3_q$2?nx$yE#hpmDYr>lkQMeFnOY}GHH zQ$MHz674*PL)yN!cW1Acw@xQ8X3K?Ne!XNrzRAy3k3K(qdI$h8GVZ6%5#Ij(``c$G zSTq3SBHfJw(8nBc8qfy`6SyF#qWCBCXh7;5;b5;w0}5crrJ>s9r%MA;=D1pQ_iBLo z$yx=CO>$E@)H-I4FcPSONsl!_N>yWu8u4?awvf7Ek_Oo6tX?#p;>OP*So7tpcP9fS zt~CM{M`nHd07`|8YhHLXz%vIgqNGh-r=9Ybq=OcXE6i zcMe%El+Un&yHa5vNJ&oDpXyJjjmr8B5w|1rY<7He2v=?oQsllo{%$*rwEqGO0H-4% U0X*PDh5!Hn07*qoM6N<$f(^lM+W-In literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/menuSlider.png b/Templates/BaseGame/game/data/ui/art/menuSlider.png new file mode 100644 index 0000000000000000000000000000000000000000..92fee1e9c13401f0b9fb319b14ebd267b7c1c8a0 GIT binary patch literal 908 zcmV;719SX|P)BsNz zeu<3YC~{d@T`O@7=Bqd8zh+r&1N$TtpK!GELCNikCSDYg%%{_tjFWkfZXnes>n;@ zqR|aj)!gZHXh8b=y;^A;kRu8#6pO{!Mvtd3L02hDKJM?<79kE%E?3>4B+0?(MwHD# znh=bupw`5N2ruq9KZs6u_7))i@#~}<;40XaEJtI*eV$Cj55Bf2ineZ<81cX|$A?-K ziRQ$vUFqvirBbh8IBB!lrWT$p@_Epw4{y{Jk<89s7(yreE_ZXgJAlQLOs#F|`{D~e zk1JU{!ht}*1pQDv9-m=Z)*TLqIgIfHJjnm+Zki*YSv0N&+<5q!9OI!(a(m|ZDWCi2 z+7hQAJOK~#FZBQBsFq0$xe508a5Ng7>+0%qd%a%d^ZA(i`uZ`3VWtE40bY>a7M7V# zB!+jkHs_9=>37>2j-V5r7a4n=; z!uTf4JA}`P;v1uD=Z~Jdb2XVYA zW9_{!_Y>&<@ydWoCaW7|c%=l304u9lXbBBDP18sKV$fMbp11YLDqKf%e#(kCvbjnH z0IY2MSwNH|Nr;Cm_2f`8%fP&(pfq1j&P&EykrGAGkevASCV(r$>yDU`^BZRlB!U6C zWJs>$^wwoAh)RXbi7W=16Mq@GgpA26$sz+uYE0GPzMbJE%h#`yBRq)9| zM)ez$XR{;UB9VaZPe36jlnmZKpDj2gSsD4W{t>WPgB5pU{)$x*B(ajOltDF2!h9-! iq)BbrE0*;C1Q-Ck2#=cQD4UW100001gDw+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/new_d.png b/Templates/BaseGame/game/data/ui/art/new_d.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfc9400d6cdba6cfa03ffbb988decd0c94904ef GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fh{`~)M&%CsO)y2Tb$f%;K>MXmFaL2_hxwpgI zc$^*BG&D3+Y$`r5EMZvB7#DX>BFQH}Du;oepa18*MyrVrpPZbW(8E1r_Uy;zEc2BX v#WFGdh%tOMDR4TE-t>gibB{1JFfcQ4Iq8;4aVT#C+REVR>gTe~DWM4fJ&8ck literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/new_h.png b/Templates/BaseGame/game/data/ui/art/new_h.png new file mode 100644 index 0000000000000000000000000000000000000000..1f9b722c1709566c6344b530f963a430bbc91e9b GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fhQW%(YxA**X&}wYtC@6hi;#OiPHi6OUQx)fX zxi}`x1KADRs@1{@)h2K|ux%*jHk!a4vA{3oM#&#ViN{QJ(?0#+Rb0}cF};DoF1%+~ v$`#e-)2cg))HgPK{Qv(y&lIKx24)7eUflw|mLHNpTNylE{an^LB{Ts5anD1? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/new_n.png b/Templates/BaseGame/game/data/ui/art/new_n.png new file mode 100644 index 0000000000000000000000000000000000000000..06531327db1ffcac89d1141c455cfde03e997068 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fhQW%*0{n!6=&}wYt*pPcW%&o*wYyu+}7Z)eL zoDGBKf$WCq)6K&Q)h2K|uzmRO!C(S+!~(w*i^@+b5|5dlJ$v?pS8+*)#`FdTyYQY} vDOXgRPpj@IQs3C{@&Et-JX4q&7?>H9ICb;qN8OSH+REVR>gTe~DWM4f=-fh< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/next-button_d.png b/Templates/BaseGame/game/data/ui/art/next-button_d.png new file mode 100644 index 0000000000000000000000000000000000000000..76c3ec0ffbfd0e390d18bf15b641406a1c963304 GIT binary patch literal 279 zcmeAS@N?(olHy`uVBq!ia0vp^+&~=2!3HE*cE)T6Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JigtRsIEGZ*dUIWo&smX&Az+(=poWapSz@o$*? zhV8dl?=!aFoKn4xeZo2Nf4=Qb$;zCp)-ipVh!6ks%DPE^D{?9q{wDx9i9DW z-@5CHEdniu-!^_}`*}dN=v;ZVvUv|*i@L#DeRZC~n>o$)T~mbY?ZFzA*KO@H&DTlVp33;PS)Y-O Z!Sa~S%jms^DnL&#c)I$ztaD0e0szoGZ1VsB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/next-button_h.png b/Templates/BaseGame/game/data/ui/art/next-button_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f52f5fb4230f07e879e02d61472b07c57d07712c GIT binary patch literal 549 zcmV+=0^0qFP)0-qXkL_v~jv zlgT7TvAv7&%t^dYr_+a#hr^+@TrOWYNQ8?)a=v3urz2xpTz;W+6qyRv^uk~=B$Fa* zY7trZc?lJ%X2T_NO4CLKXgbTNCUvD{m}Vo^YPY~Tkd2ce$z(`A zSFm{3{j7pUGGsRDL1tMpV+>&!sySO(N(;fVWY6>Px?ZdKIygr@9z84;3mir8H9bcT zKSQ{#tA_9UkIBuZg(!-!TCLy*m)dh=+qMwLF?PEhHk&s@;`3dyZQCkM?RFd6?G_i? zH&3?FXf)96b}<|dg{B^9J^vSnF{HXe^vpq8OGKg(`@ zDPc4kq4(575CoTb2I&Kvq)6s=Hk-l!@S!1z#rXq!I-TG+j+$?6tXwP5KmRLDro?qz zvLl(u&>hLB$=P8+ zw(YFdb&VZ&3=k2N&8;vD6%z#@SPPD4Vq~7_lP=Gm!*~tPGFXzjSBY6BP=OAZn1#Yz zeMOAFs`Cm16Yd2fQ=s2N5ee+0alltX+9{%Ju=RO7?f|t1WmQ!f&x7^dJ=)Ck+zLbQ z8C%e;EK4{X{$aeYESPxc)^_`2^&U`(>ag;@-~WwZxO#4HMyn_aV}GbRTk76dt6?AK zsxC-Q@=mGD}pHXHn{a?_1VZvZ|8^e0u7drcunPkNJ1EWBngI a3oroff;ABVIBm%Q00000druqyUGAf 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/numericslider.png b/Templates/BaseGame/game/data/ui/art/numericslider.png new file mode 100644 index 0000000000000000000000000000000000000000..6a97d60c8429d871b9f16a5915f6b24fc1abda35 GIT binary patch literal 993 zcmV<710MW|P)ShNeqfsEQkt5(y#U7Lj3w`n1S<0gWEh=_?+(GQ54XwqmZwHYl) zZSFbxo=@Mi0rleHo_p@`ymRjT-gh4N3c!A~4uk8$LKV z2nnL$DVh`z@fyCw zC&93Ed3g!*^YgS`T3Q0FR$yQ>t7gv2GZN(~{RFYt4b08W!P(gv3F>IWJ-M$U5N3qJ zF&tq@NG+$Qr{H$GNl-@{Ly+VvuR?Z4yoBt-!$a_Ty(CjpQ)FLRS)uvj;wSWd{2&2V z5lGy?n;0GS!PwYn39=U!zLp?c!A*7LY^?n;nW!p&smTn%V6a$PHkXq&)9EzC<8jiF zLU7#P-qNhs>%n5NR39Djp~82x%{i3 zZP0?#>BR0pycCT_$&PvpquGIh0njz-B!2~tFe6bAy%Q4?5;KZSi8hDBLDwVtDgxmX zL_u=H5~io8!EU#cppG`&llv-5lSLQT*g0lrXQlpz=T~wrvR)@?9UUFjc3L7g3WPYQ zl_BfP*+{j2|2$R|;GNL~fj~g{(&9*^QgDBt@33}zzTYDk;lOU&+-$9kY%x%p5m)$I zMC$qDR1i#-8CX)%c zN>mG&Q9*3)ZLqesCNZPPlxXYe>CSf)(O>adBO3@{fxtsuPp zSyP^B1N(!cy&X0;H)(BV<&-0ZQ_D!FH>f_Tjb-`~(y literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/previous-button_d.png b/Templates/BaseGame/game/data/ui/art/previous-button_d.png new file mode 100644 index 0000000000000000000000000000000000000000..688b3034593019b560c95d56ca9ad50633a1d800 GIT binary patch literal 290 zcmV+-0p0$IP)aLQWp}M-VW=GgSOtJO1OWsQ!>dPPUAR&lL*PV&z_HIo(bxiQo zG};(LQc4m+6k(jEjJ36vwAQ4Q+D*=|s;Zp)s}5;NiF@#a(PGS)F=NIkUe^`Jecxkv zS(e-o9)BL_eXU0B$9lCDhj0MLO$6qS*o3}t8TfCx>}q<#)g6KIH2DOD=f(ZiAb^zpyn zi5)f=3~mA3^Qa@I>5M1#`~A9Y+gB5((3 zy3Q2IOpqltj~r`OQ*qL)SfWmr$+)hfTDpz0?$jW9A znJJRv70iF?ezt;2GRTc;khLUPhNfxoeP8r3h{ls-8II%Nd9@OKJpLV-Xa}A&o6T`J zI4SalX4iGGUazs;zQX_TfVHg+=hoVTjm&s{2v?w?ly z*zfl+&4gw~WTf37V)v$tcDs#Qt(E{W`NC?cXTyDWF&d48=QKzEmifXSk4MnKPknMc zb1NV@$+2JAWC|R|1$QJB8FEKb+GN=OF;Pu&sb7pz%GFO|)dJg)641pwaK zh{1KW@K4h;sFYfamcsxM<0y(26Sjjr)t45dZ)H07*qoM6N<$f@y!$IRF3v literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/radioButton.png b/Templates/BaseGame/game/data/ui/art/radioButton.png new file mode 100644 index 0000000000000000000000000000000000000000..d5ecc98534ef4cd9e716337a68ab54ca69b31687 GIT binary patch literal 843 zcmV-R1GM~!P)tR>=AQ4|ob#P8pJD^_s{YYn+X`<+M@Mz~c&=K;Qz*(E8MW`}_6o?rtgs}N8n6llnV<>*O><8dJA&R-;R$eGGkf!tG=Y!MXM00a9mX?-+Me!XU9TMmODosVtZ@Ap$FaT%zoslnjjpmgt2@p02W^3Mn1a9GxQx2+A) zjt&eA3?S0pj;5w2c)ebBZcx)NwM+3mqZ;*SG%DRMa615p!$H4NE4}sKitmR20|2im V3rY;NUd;di002ovPDHLkV1nBllNbO1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/scrollBar.png b/Templates/BaseGame/game/data/ui/art/scrollBar.png new file mode 100644 index 0000000000000000000000000000000000000000..e8c34dc857ea46068c4058709b4c60ee02cc1f20 GIT binary patch literal 3332 zcmV+f4g2zmP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf02y>eSaefwW^{L9 za%BKPWN%_+AW3auXJt}lVPtu6$z?nM01R|VL_t(|UhQ3Jj1q_F05fsc6Mev?|Vy)dS<%1d(NekdD>E`yQ;e1ysEDE^{cOX4U>rfK727AJa}OC z?%kULfdF;r(1G&u@+9e`wY8N3T>kGncj&~46U6OZf90@Y!)V2d6;xDQOx0CYR905z z+D28VuCAuCUcCr6GVAK<%!Y;rv$?t14243blZ3-z6TPr$(y={@83`MR?;qMMb%v~6!_O` z6Ux&eDJ!a!tgWkKt5wp4iL)MkC5vm&Z1|}o~65Y?@E2-ZpyxU_l_DGn`rm$J#_WzRcVJD$2gC2 zCxLRcwY5?OxlY+AMbDoQu?ylnsYl#rhX6T%aypy3ndst0c!U&ZAr> zN=ZzOefso~DN?6w2X&EPFi1m&3?a;UUAuOrVxIH*_wP?Ub!l%T-D(&#XU-f+ef##M zo;`Zdgb5QOmdP*3w{4GythBULQht8Eq|Ti?Q%g&WZT*Nv3knM4M*aHr6D1+Yag1aq z1}kZB!6zd|NGjp_O-)Ubt8>BTp#6>6dQc1rN

y=B0vFoY*eFT;806|)ERK!%dbrTa%1TK9Bjd-9 zr^}Zwi-N<44;PRUD|p8mJa{mjI(14AQ>RXy1kGcDhbtX7ZX8XXJXzkw;#?xT(6?{j z3J3uh>Ba9w{MtcKu%x6!T&t}`aS|7W2Z3^ccTP&GivWbIUcH(oO`0Ui9XxnYavbA4 z%5?&-1*M?e%a<=BO5(h$uP-kz7Yjmkmb6|ZFE2pFMP2CIHEU?vv}sa=9LG41awk#B zq7Ia{QxaviS3x(c2?E@105;E zR9{~&)}l)PttOOhZ4I|2jx8a+gcuZitEhD4$`!Ms zqC#Rpix)38j~_p7vL`X?Id;V^)8uz~lf9ey`0?YYbl|`NbLrBhsl}e~gpM6MmSaf# z4vshyr^beQs(JmIo;`aOl}3*qEj}EA0K<$KpHnmYb-2Cy`a0>A>greGimhql#EIf{ zQjw&e`c!V3Oxq|YTv72CD&=tqUmt1-QPA>^J$LS0?e%#WP%j>b2t4qxk)&k#^5x0s z71t(8Q9LZ8M*WU3_*)q908x1nc$Lp*%%DMo2E{4rrM;IrQC4khIDr8hqD>W0WMK?v!wXo*Sox6w^%mnQ(TC_-zKFZS8^{EVS zw8ffyettoOj)7h%>FMkg2ntlMeRelK&RT}s7JUfZ@!Pi-cVw4ae$%~oO))8A$|GfAL#3^{}?&;db3pOiK3Vbm7-lsLCqW$rzmzSf~tuG+U5+V zVXEU)6!6+MGU2Q63p@-Ii4Dgv|CzlIPvV?FXa!eP9ecf5D)rhdDqD7VZzm^s6_5ed zvATFL*uvwpQIcZas^e4?@Y+@sZRN44Hk?X#VqekwO{wZa04bQlkd&h8*z3(wspp$j z5&tR*cDQ0W=(-9_a6EL`lmL_h1w*(07|LK40No;%f}xJHp|J7v=~MI2p+k`!3$Zta zudn2Hr!Bl(quZU34PWn{l{=8Jow`mDubt{-#iH18 z8mX&%Art`E>G4is6t7)JdqnF zTQ{Ikk}k)E6-v_O$=*DXcE(HIdgj!iH}mZ9Z6_!B?Z}atAEOE->2fHjP?9cB#4^d& z^)Dz%_7`Wmoo_5^VDG1sH4sUD!e1e9l7Sz8obiXr@Qttc&B~q5qToJHnKFgOefF7b zTeB1JoR=+IM&-N`?Y735a_lLxXE`S3g#FBjOTps0m=oerh)ZQ9_7odyQpbo#sU70F zm=oerUUq}0W9o{4lDL?YWiHUgoDh%7mP8bdjX7E8EM3eA@u+M`1U>0uPFD038*@TD zDq9lJ#heh2Qlg7FAs&@23Fu-@h({^W#hmQEuahaoedFu>r|5?ob5cVopo=+Kw*={8 zPKZY#E|m?@q9+8;7A#mm13vml>+7N9`t|GSHykF3+Fsfd5jlj}TcX9f=zv}*>dx>;V=&NpN9e@jtbDyKdCUjy1xJXsWE znAx*ux2K+D`2Jx+ck@Rk#fmp?-lW5a54WpDwOtRsGCpq%!8$1@@9io6?0Rb#Tc7lk zCr=XojYoS*RM+E^-Kx7^y?RCS=g$}25H%UW|2(2S#h+c@o)&Yv_kRJO_n_Zf_&8bs O0000P)OT4`WyUtC~=@hx+zySvwaKHgKA>83`kd%_BYOLNgO(Uv$2b*)@|Eg9YW@7fI zT~!q^i<8!-Y2N9)S2Rgnw#U*CJ*lmP+UI&jej#f@hhX^j%SM~?LF zi?zW-dhDh9K8FR%|AW1+Pd7{fT$3IAR&V6+TZaPUw@w9!-|CGXdJee7;UfF3q}2_F z-|E$KyQ*vs6_t}znHa6W?Ga9$rG&H;mg0WQq!gDq*w&w<$RMR-QTF zp5X9X$D|d%wE|28zjYYM_QYn*#rUnC;P6`qMiswx)bfSjir571FWj#7x87#*oU@1w z+uvGBam@&64A{^k=j;-))qZO&*qHn9Tm5kCB=ITlrQvKh0|K};_P1&c*wXu3jc4w^ zg=2r~8qq_QY(Sd1r literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/selector-button-dark.png b/Templates/BaseGame/game/data/ui/art/selector-button-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..84ee7e6f30f4eef5b3d2a1440495b16cf3b21f65 GIT binary patch literal 1942 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 LNkvXXu0mjfl=J=< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/selector-button.png b/Templates/BaseGame/game/data/ui/art/selector-button.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0780068892795e6329d713f48e84fa9f1df8d8 GIT binary patch literal 4002 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 IM6N<$f{4Jk(*OVf literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/separator-h.png b/Templates/BaseGame/game/data/ui/art/separator-h.png new file mode 100644 index 0000000000000000000000000000000000000000..339c0fbe039e8b5cbdd085230594099c26928157 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^Oh7Ed0V0oZ{G0)#BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%iF>*@hE&{2%E-w0@&CWQFtY@YFPFjrhT;U4EtAg|X#kZl Nc)I$ztaD0e0sy9+AwB>A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/separator-v.png b/Templates/BaseGame/game/data/ui/art/separator-v.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0f873612cef214add9a2f88ed72dbafe92174c GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^GC<74!2~3aY}VEQQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JiX=Q;978H@CFNvfBrGsX`1Ak2{Z(F84g-d-`&k-}TiGQ5 P)i8Lv`njxgN@xNAx)CBs literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/slider-w-box.png b/Templates/BaseGame/game/data/ui/art/slider-w-box.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ef04961a30ad99f1e8871ef1f292b8a58d99f0 GIT binary patch literal 982 zcmV;{11bE8P)P001Qj1^@s6w^2g10000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#cu7P-RCwCFR!vM3K@@(oZI`u_7W)HS z_z46E5)-2a4+bql1U-1bs3#7@MD)a635mu~6G-Gn0+OI7O^h)fNi@NO!AOt<2?u@- z9*T%*X=%IvU1wGbyW4iRJ=m8_JMZl``|W!(^UWYsCMfWq4Q+02T9P!k*TKfx&%D0b zIAV74Q(YCX9ALy)joJOZchpx&6vet?Mob}N^dtoU6${^z1Pb{oBc|v~N>Z->n@DC( z^dn?hhVAVwNE{p-MuM=syre-K`>u)#Xl!f(o6S~Sxwf`8L6&6j`TR#QZC~prlSvid z+}u(e$w530ZEfwknBO1B>68EU^)=Huvgcc$9*%OPjCcpe23-4*Ecl4pS?Z!vATK~Iiu<8eTHFFyaGybOBf*v0#((zSZ!Ussc*KV zOSc(IBP*p4NV1{K&1b;)3K*lE;}RUd_RdRpkQHcQ{vA+^sH8cEioJZbKQB(*d>ETm zuGaa}MSa=Fv8LiO$#h;y@AHDD?T@o!>D`6mNv2*6_aqXDx41j*a=9k%Ha_7s%C58F zFXwu=WDCZkai-igF-SdCvFG!V4BWm>!3*e*>_?`8!JubxadCuYS^C-62cdY{%V|mO zPrfEv*U!cyQ>QNWdN!6mk8mtUKTi&Y8cr}=J`yS#jShsv;o0u)ZhuWp4fOW*GG4ED z6l>Dt*u#5hb>-U;I2DTx9PIAQwsqd{y8?C4-gT3yI9WaFU@BB>UKK8*>A7oteeO!E zq@o~z9iMO)Iy!(tXb3;o>AT`6pKHBx%T2qhfX53+u`y74whPk8o`uzwE162EM<|a8 zw7SIcYMIM%i)G?$iUZ2&2bNcK>ca7I7FI{%a5zA~1PCEZ@l)(h;K<%ZdJU70*H7+D zBuN4uf3=Z>TqcahOA4g;Y!Y6wSi+(x<|H9;qX{^~k}4v-QVOC4iG*J!WFkKE!Y<#Qq8}0K%2(y+C}?TL1t607*qoM6N<$ Eg3Ob{L;wH) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/slider.png b/Templates/BaseGame/game/data/ui/art/slider.png new file mode 100644 index 0000000000000000000000000000000000000000..92fee1e9c13401f0b9fb319b14ebd267b7c1c8a0 GIT binary patch literal 908 zcmV;719SX|P)BsNz zeu<3YC~{d@T`O@7=Bqd8zh+r&1N$TtpK!GELCNikCSDYg%%{_tjFWkfZXnes>n;@ zqR|aj)!gZHXh8b=y;^A;kRu8#6pO{!Mvtd3L02hDKJM?<79kE%E?3>4B+0?(MwHD# znh=bupw`5N2ruq9KZs6u_7))i@#~}<;40XaEJtI*eV$Cj55Bf2ineZ<81cX|$A?-K ziRQ$vUFqvirBbh8IBB!lrWT$p@_Epw4{y{Jk<89s7(yreE_ZXgJAlQLOs#F|`{D~e zk1JU{!ht}*1pQDv9-m=Z)*TLqIgIfHJjnm+Zki*YSv0N&+<5q!9OI!(a(m|ZDWCi2 z+7hQAJOK~#FZBQBsFq0$xe508a5Ng7>+0%qd%a%d^ZA(i`uZ`3VWtE40bY>a7M7V# zB!+jkHs_9=>37>2j-V5r7a4n=; z!uTf4JA}`P;v1uD=Z~Jdb2XVYA zW9_{!_Y>&<@ydWoCaW7|c%=l304u9lXbBBDP18sKV$fMbp11YLDqKf%e#(kCvbjnH z0IY2MSwNH|Nr;Cm_2f`8%fP&(pfq1j&P&EykrGAGkevASCV(r$>yDU`^BZRlB!U6C zWJs>$^wwoAh)RXbi7W=16Mq@GgpA26$sz+uYE0GPzMbJE%h#`yBRq)9| zM)ez$XR{;UB9VaZPe36jlnmZKpDj2gSsD4W{t>WPgB5pU{)$x*B(ajOltDF2!h9-! iq)BbrE0*;C1Q-Ck2#=cQD4UW100001gDw+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/tab-border.png b/Templates/BaseGame/game/data/ui/art/tab-border.png new file mode 100644 index 0000000000000000000000000000000000000000..6703924d48fb96552fe11a91696f7c282aea3439 GIT binary patch literal 1203 zcmV;k1WfyhP)s+T@)zO{{b^^V< z=wH7wY(L{2P71F802ipY7kw@qE$vBN%q#5`37@CZ`H-O1-w@Umr|4D%f4*L^Nt+hhLK_6p2Z z%jmzE=C=LL3e%yyDGwhjj6U6g3bP`N-;PET+g?oxjXD4|r+B4=CaKr15}L$b?q6b0 zh8~SA%A;{SQ}t+Cpt=UfVlxhaEO#3>tT*c#8_eR@?>5%(PLkvwCV9zbSc8!+a4ONV zR-+KOJO_Zt7V|Std?$P6?aK-C`OA{+V;uY;3(0FXgFHquf);&@Yc?a<)A*xjHOZUF z9OEwblp3gHGX%+c&yuRyjAl==LJ=%0nk$@AZ@u)S6zSP8LLos?q-QfZ^*VWDa!G;yi`d#otZB4azuR)9UhD?F)aac|0iuXn*Eh1*_F zk5@SQ_F3yW*%QGMWE}P+4H9G+d(!r(D?=_|4>u*qe>cw_ShR}v@)e(9wdwjG#i_h1~oIT;xGv>0sd%EDSr z1<%~X^wgl##FWaylc_cg49vcsE{-7;x8BS;-6iEH(&k@&B(hUU!{sFFrdeA}-npDm z+T0HL~qg_GQ82uO0A(c*t_B$Bg|A+hBqOAX`oNY)}p=B6D5%h%3b^=Z7j%x(QBs}%ka&;nDJ!IQMaC;qNArjKlu4!Pet9K=Z8y&$_0r{Sal%;m-QK=DfhLGD9wQDT9iS|uLbH|n?#9ZCG~{-5=>9j4w~ zGdIsY-0mMQ_Vec+MH%Z&?2@N9>1F(WXJLHWWa><(NAGr|=8Cqwe3ntDfA7A@!}8Uq zpB8Nm(3r69^%M`4mdA;D?)ATFQ=fHM-}_i`W?#bdD{sqIUVa&47!bNYI{T;rkC)oy zY=KqX&kufnSSiSL=yYO#(}wJ7M@NeruPgMlvRkj;a@62>?96MxzzjpmTZEpgZH;={ zw^%Wge>I<=KGz{><->(0o`we|{yMZG_G6L!H6EP>4U7_4kYMgnzx(^Xmzc~vS(A8W zUB&FW#5r?pzJI%-@A-;*vC7AL_v*ghW#u{)VT`a4ZZj%F;Q#{@x~oUi}@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/textEdit.png b/Templates/BaseGame/game/data/ui/art/textEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..5a65fac3cedf36c09e5add1c874775d8e364b343 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^{2(_@_GbJ<_dZw^Q+&I7EPrEnI(eqE51YbYz7T13zwEE$$n>MSVv_3!WR xqwIV#4BRgm4JXvHMKX&S7St`!IKaTf!=UB9TBWi;$P?&J22WQ%mvv4FO#lthVzU4M literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/textEditSliderBox.png b/Templates/BaseGame/game/data/ui/art/textEditSliderBox.png new file mode 100644 index 0000000000000000000000000000000000000000..57a0c49d31811ff357bbc97b755a1324f86ecca4 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^{2}1{W*Vt!Q~0#wD#}s4RdbqW7eB6bLvrp+FK?f zt}l|}=E?DV>hj{@xxCExH~$gd|NsA|GbFPKu%;#pGOuKP$+%nM%z=dh(HqXjD9}9&p00i_>zopr0D_oRm;e9( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/art/window.png b/Templates/BaseGame/game/data/ui/art/window.png new file mode 100644 index 0000000000000000000000000000000000000000..d9e8006e473974be6c97716e9ab9abc2e0e373e3 GIT binary patch literal 2559 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.clear(); + + %count = LevelFilesList.count(); + for ( %i=0; %i < %count; %i++ ) + { + %file = LevelFilesList.getKey( %i ); + if ( !isFile(%file @ ".mis") && !isFile(%file) ) + continue; + + // Skip our new level/mission if we arent choosing a level + // to launch in the editor. + if ( !%this.launchInEditor ) + { + %fileName = fileName(%file); + if (strstr(%fileName, "newMission.mis") > -1 || strstr(%fileName, "newLevel.mis") > -1) + continue; + } + + %this.addMissionFile( %file ); + } + + // Also add the new level mission as defined in the world editor settings + // if we are choosing a level to launch in the editor. + if ( %this.launchInEditor ) + { + %file = EditorSettings.value( "WorldEditor/newLevelFile" ); + if ( %file !$= "" ) + %this.addMissionFile( %file ); + } + + // Sort our list + CL_levelList.sort(0); + + // Set the first row as the selected row + CL_levelList.setSelectedRow(0); + + for (%i = 0; %i < CL_levelList.rowCount(); %i++) + { + %preview = new GuiButtonCtrl() { + profile = "GuiMenuButtonProfile"; + internalName = "SmallPreview" @ %i; + Extent = "368 35"; + text = getField(CL_levelList.getRowText(%i), 0); + command = "ChooseLevelWindow.previewSelected(ChooseLevelWindow->SmallPreviews->SmallPreview" @ %i @ ");"; + buttonType = "RadioButton"; + }; + + ChooseLevelWindow->SmallPreviews.add(%preview); + + %rowText = CL_levelList.getRowText(%i); + + // Set the level index + %preview.levelIndex = %i; + + // Get the name + %name = getField(CL_levelList.getRowText(%i), 0); + + %preview.levelName = %name; + + %file = getField(CL_levelList.getRowText(%i), 1); + + // Find the preview image + %levelPreview = getField(CL_levelList.getRowText(%i), 3); + + // Test against all of the different image formats + // This should probably be moved into an engine function + if (isFile(%levelPreview @ ".png") || + isFile(%levelPreview @ ".jpg") || + isFile(%levelPreview @ ".bmp") || + isFile(%levelPreview @ ".gif") || + isFile(%levelPreview @ ".jng") || + isFile(%levelPreview @ ".mng") || + isFile(%levelPreview @ ".tga")) + { + %preview.bitmap = %levelPreview; + } + + // Get the description + %desc = getField(CL_levelList.getRowText(%i), 2); + + %preview.levelDesc = %desc; + } + + ChooseLevelWindow->SmallPreviews.firstVisible = -1; + ChooseLevelWindow->SmallPreviews.lastVisible = -1; + + if (ChooseLevelWindow->SmallPreviews.getCount() > 0) + { + ChooseLevelWindow->SmallPreviews.firstVisible = 0; + + if (ChooseLevelWindow->SmallPreviews.getCount() < 6) + ChooseLevelWindow->SmallPreviews.lastVisible = ChooseLevelWindow->SmallPreviews.getCount() - 1; + else + ChooseLevelWindow->SmallPreviews.lastVisible = 4; + } + + if (ChooseLevelWindow->SmallPreviews.getCount() > 0) + ChooseLevelWindow.previewSelected(ChooseLevelWindow->SmallPreviews.getObject(0)); + + // If we have 5 or less previews then hide our next/previous buttons + // and resize to fill their positions + if (ChooseLevelWindow->SmallPreviews.getCount() < 6) + { + ChooseLevelWindow->PreviousSmallPreviews.setVisible(false); + ChooseLevelWindow->NextSmallPreviews.setVisible(false); + + %previewPos = ChooseLevelWindow->SmallPreviews.getPosition(); + %previousPos = ChooseLevelWindow->PreviousSmallPreviews.getPosition(); + + %previewPosX = getWord(%previousPos, 0); + %previewPosY = getWord(%previewPos, 1); + + ChooseLevelWindow->SmallPreviews.setPosition(%previewPosX, %previewPosY); + + ChooseLevelWindow->SmallPreviews.colSpacing = 10;//((getWord(NextSmallPreviews.getPosition(), 0)+11)-getWord(PreviousSmallPreviews.getPosition(), 0))/4; + ChooseLevelWindow->SmallPreviews.refresh(); + } + + /*if (ChooseLevelWindow->SmallPreviews.getCount() <= 1) + { + // Hide the small previews + ChooseLevelWindow->SmallPreviews.setVisible(false); + + // Shrink the ChooseLevelWindow so that we don't have a large blank space + %extentX = getWord(ChooseLevelWindow.getExtent(), 0); + %extentY = getWord(ChooseLevelWindow->SmallPreviews.getPosition(), 1); + + ChooseLevelWIndow.setExtent(%extentX, %extentY); + } + else + { + // Make sure the small previews are visible + ChooseLevelWindow->SmallPreviews.setVisible(true); + + %extentX = getWord(ChooseLevelWindow.getExtent(), 0); + + %extentY = getWord(ChooseLevelWindow->SmallPreviews.getPosition(), 1); + %extentY = %extentY + getWord(ChooseLevelWindow->SmallPreviews.getExtent(), 1); + %extentY = %extentY + 9; + + //ChooseLevelWIndow.setExtent(%extentX, %extentY); + //}*/ +} + +function ChooseLevelDlg::addMissionFile( %this, %file ) +{ + %levelName = fileBase(%file); + %levelDesc = "A Torque level"; + + %LevelInfoObject = getLevelInfo(%file); + + if (%LevelInfoObject != 0) + { + if(%LevelInfoObject.levelName !$= "") + %levelName = %LevelInfoObject.levelName; + else if(%LevelInfoObject.name !$= "") + %levelName = %LevelInfoObject.name; + + if (%LevelInfoObject.desc0 !$= "") + %levelDesc = %LevelInfoObject.desc0; + + if (%LevelInfoObject.preview !$= "") + %levelPreview = %LevelInfoObject.preview; + + %LevelInfoObject.delete(); + } + + CL_levelList.addRow( CL_levelList.rowCount(), %levelName TAB %file TAB %levelDesc TAB %levelPreview ); +} + +function ChooseLevelDlg::onSleep( %this ) +{ + // This is set from the outside, only stays true for a single wake/sleep + // cycle. + %this.launchInEditor = false; +} + +function ChooseLevelWindow::previewSelected(%this, %preview) +{ + // Set the selected level + if (isObject(%preview) && %preview.levelIndex !$= "") + CL_levelList.setSelectedRow(%preview.levelIndex); + else + CL_levelList.setSelectedRow(-1); + + // Set the large preview image + if (isObject(%preview) && %preview.bitmap !$= "") + %this->CurrentPreview.setBitmap(%preview.bitmap); + else + %this->CurrentPreview.setBitmap("data/ui/art/no-preview"); + + // Set the current level name + if (isObject(%preview) && %preview.levelName !$= "") + %this->LevelName.setText(%preview.levelName); + else + %this->LevelName.setText("Level"); + + // Set the current level description + if (isObject(%preview) && %preview.levelDesc !$= "") + %this->LevelDescription.setText(%preview.levelDesc); + else + %this->LevelDescription.setText("A Torque Level"); +} + +function ChooseLevelWindow::previousPreviews(%this) +{ + %prevHiddenIdx = %this->SmallPreviews.firstVisible - 1; + + if (%prevHiddenIdx < 0) + return; + + %lastVisibleIdx = %this->SmallPreviews.lastVisible; + + if (%lastVisibleIdx >= %this->SmallPreviews.getCount()) + return; + + %prevHiddenObj = %this->SmallPreviews.getObject(%prevHiddenIdx); + %lastVisibleObj = %this->SmallPreviews.getObject(%lastVisibleIdx); + + if (isObject(%prevHiddenObj) && isObject(%lastVisibleObj)) + { + %this->SmallPreviews.firstVisible--; + %this->SmallPreviews.lastVisible--; + + %prevHiddenObj.setVisible(true); + %lastVisibleObj.setVisible(false); + } +} + +function ChooseLevelWindow::nextPreviews(%this) +{ + %firstVisibleIdx = %this->SmallPreviews.firstVisible; + + if (%firstVisibleIdx < 0) + return; + + %firstHiddenIdx = %this->SmallPreviews.lastVisible + 1; + + if (%firstHiddenIdx >= %this->SmallPreviews.getCount()) + return; + + %firstVisibleObj = %this->SmallPreviews.getObject(%firstVisibleIdx); + %firstHiddenObj = %this->SmallPreviews.getObject(%firstHiddenIdx); + + if (isObject(%firstVisibleObj) && isObject(%firstHiddenObj)) + { + %this->SmallPreviews.firstVisible++; + %this->SmallPreviews.lastVisible++; + + %firstVisibleObj.setVisible(false); + %firstHiddenObj.setVisible(true); + } +} + +// Do this onMouseUp not via Command which occurs onMouseDown so we do +// not have a lingering mouseUp event lingering in the ether. +function ChooseLevelDlgGoBtn::onMouseUp( %this ) +{ + // So we can't fire the button when loading is in progress. + if ( isObject( ServerGroup ) ) + return; + + // Launch the chosen level with the editor open? + if ( ChooseLevelDlg.launchInEditor ) + { + activatePackage( "BootEditor" ); + ChooseLevelDlg.launchInEditor = false; + StartGame("", "SinglePlayer"); + } + else + { + StartGame(); + } +} + diff --git a/Templates/BaseGame/game/data/ui/scripts/controlsMenu.cs b/Templates/BaseGame/game/data/ui/scripts/controlsMenu.cs new file mode 100644 index 000000000..f9718e647 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/controlsMenu.cs @@ -0,0 +1,417 @@ +// ============================================================================= +// KEYBINDS MENU +// ============================================================================= +$RemapCount = 0; +$RemapName[$RemapCount] = "Forward"; +$RemapCmd[$RemapCount] = "moveforward"; +$RemapGroup[$RemapCount] = "Movement"; +$RemapCount++; +$RemapName[$RemapCount] = "Backward"; +$RemapCmd[$RemapCount] = "movebackward"; +$RemapGroup[$RemapCount] = "Movement"; +$RemapCount++; +$RemapName[$RemapCount] = "Strafe Left"; +$RemapCmd[$RemapCount] = "moveleft"; +$RemapGroup[$RemapCount] = "Movement"; +$RemapCount++; +$RemapName[$RemapCount] = "Strafe Right"; +$RemapCmd[$RemapCount] = "moveright"; +$RemapGroup[$RemapCount] = "Movement"; +$RemapCount++; +$RemapName[$RemapCount] = "Jump"; +$RemapCmd[$RemapCount] = "jump"; +$RemapGroup[$RemapCount] = "Movement"; +$RemapCount++; + +$RemapName[$RemapCount] = "Fire Weapon"; +$RemapCmd[$RemapCount] = "mouseFire"; +$RemapGroup[$RemapCount] = "Combat"; +$RemapCount++; +$RemapName[$RemapCount] = "Adjust Zoom"; +$RemapCmd[$RemapCount] = "setZoomFov"; +$RemapGroup[$RemapCount] = "Combat"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Zoom"; +$RemapCmd[$RemapCount] = "toggleZoom"; +$RemapGroup[$RemapCount] = "Combat"; +$RemapCount++; + +$RemapName[$RemapCount] = "Free Look"; +$RemapCmd[$RemapCount] = "toggleFreeLook"; +$RemapGroup[$RemapCount] = "Miscellaneous"; +$RemapCount++; +$RemapName[$RemapCount] = "Switch 1st/3rd"; +$RemapCmd[$RemapCount] = "toggleFirstPerson"; +$RemapGroup[$RemapCount] = "Miscellaneous"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Camera"; +$RemapCmd[$RemapCount] = "toggleCamera"; +$RemapGroup[$RemapCount] = "Miscellaneous"; +$RemapCount++; + +function ControlsMenu::onWake(%this) +{ + ControlSetList.clear(); + ControlSetList.add( "Movement", "Movement" ); + ControlSetList.add( "Combat", "Combat" ); + ControlSetList.add( "Miscellaneous", "Miscellaneous" ); + + ControlSetList.setSelected( "Movement", false ); + + ControlsMenuOptionsArray.clear(); + ControlsMenu.loadGroupKeybinds("Movement"); + ControlsMenuOptionsArray.refresh(); +} + +function ControlSetList::onSelect( %this, %id, %text ) +{ + ControlsMenuOptionsArray.clear(); + + if(%text $= "Movement") + ControlsMenu.loadGroupKeybinds("Movement"); + else if(%text $= "Combat") + ControlsMenu.loadGroupKeybinds("Combat"); + else if(%text $= "Miscellaneous") + ControlsMenu.loadGroupKeybinds("Miscellaneous"); + + ControlsMenuOptionsArray.refresh(); +} + +function ControlsMenuOKButton::onClick(%this) +{ + // write out the control config into the keybinds.cs file + %prefPath = getPrefpath(); + moveMap.save( %prefPath @ "/keybinds.cs" ); + + OptionsMenu.backOut(); +} + +function ControlsMenuDefaultsButton::onClick(%this) +{ + //For this to work with module-style, we have to figure that somewhere, we'll set where our default keybind script is at. + //This can be hardcoded in your actual project. + exec($KeybindPath); + ControlsMenu.reload(); +} + +function ControlsMenu::loadGroupKeybinds(%this, %keybindGroup) +{ + %optionIndex = 0; + for(%i=0; %i < $RemapCount; %i++) + { + //find and add all the keybinds for the particular group we're looking at + if($RemapGroup[%i] $= %keybindGroup) + { + %temp = %this.getKeybindString(%i); + + %option = %this.addKeybindOption(); + %option-->nameText.setText($RemapName[%i]); + %option-->rebindButton.setText(%temp); + %option-->rebindButton.keybindIndex = %i; + %option-->rebindButton.optionIndex = %optionIndex; + %optionIndex++; + } + } +} + +function ControlsMenu::addKeybindOption(%this) +{ + %tamlReader = new Taml(); + + %graphicsOption = %tamlReader.read("data/ui/scripts/guis/controlsMenuSetting.taml"); + + ControlsMenuOptionsArray.add(%graphicsOption); + + return %graphicsOption; +} + +function ControlsMenu::getKeybindString(%this, %index ) +{ + %name = $RemapName[%index]; + %cmd = $RemapCmd[%index]; + + %temp = moveMap.getBinding( %cmd ); + if ( %temp $= "" ) + return %name TAB ""; + + %mapString = ""; + + %count = getFieldCount( %temp ); + for ( %i = 0; %i < %count; %i += 2 ) + { + %device = getField( %temp, %i + 0 ); + %object = getField( %temp, %i + 1 ); + + %displayName = %this.getMapDisplayName( %device, %object ); + + if(%displayName !$= "") + { + %tmpMapString = %this.getMapDisplayName( %device, %object ); + + if(%mapString $= "") + { + %mapString = %tmpMapString; + } + else + { + if ( %tmpMapString !$= "") + { + %mapString = %mapString @ ", " @ %tmpMapString; + } + } + } + } + + return %mapString; +} + +function ControlsMenu::redoMapping( %device, %action, %cmd, %oldIndex, %newIndex ) +{ + //%actionMap.bind( %device, %action, $RemapCmd[%newIndex] ); + moveMap.bind( %device, %action, %cmd ); + + %remapList = %this-->OptRemapList; + %remapList.setRowById( %oldIndex, buildFullMapString( %oldIndex ) ); + %remapList.setRowById( %newIndex, buildFullMapString( %newIndex ) ); +} + +function ControlsMenu::getMapDisplayName( %this, %device, %action ) +{ + if ( %device $= "keyboard" ) + return( %action ); + else if ( strstr( %device, "mouse" ) != -1 ) + { + // Substitute "mouse" for "button" in the action string: + %pos = strstr( %action, "button" ); + if ( %pos != -1 ) + { + %mods = getSubStr( %action, 0, %pos ); + %object = getSubStr( %action, %pos, 1000 ); + %instance = getSubStr( %object, strlen( "button" ), 1000 ); + return( %mods @ "mouse" @ ( %instance + 1 ) ); + } + else + error( "Mouse input object other than button passed to getDisplayMapName!" ); + } + else if ( strstr( %device, "joystick" ) != -1 ) + { + // Substitute "joystick" for "button" in the action string: + %pos = strstr( %action, "button" ); + if ( %pos != -1 ) + { + %mods = getSubStr( %action, 0, %pos ); + %object = getSubStr( %action, %pos, 1000 ); + %instance = getSubStr( %object, strlen( "button" ), 1000 ); + return( %mods @ "joystick" @ ( %instance + 1 ) ); + } + else + { + %pos = strstr( %action, "pov" ); + if ( %pos != -1 ) + { + %wordCount = getWordCount( %action ); + %mods = %wordCount > 1 ? getWords( %action, 0, %wordCount - 2 ) @ " " : ""; + %object = getWord( %action, %wordCount - 1 ); + switch$ ( %object ) + { + case "upov": %object = "POV1 up"; + case "dpov": %object = "POV1 down"; + case "lpov": %object = "POV1 left"; + case "rpov": %object = "POV1 right"; + case "upov2": %object = "POV2 up"; + case "dpov2": %object = "POV2 down"; + case "lpov2": %object = "POV2 left"; + case "rpov2": %object = "POV2 right"; + default: %object = ""; + } + return( %mods @ %object ); + } + else + error( "Unsupported Joystick input object passed to getDisplayMapName!" ); + } + } + + return( "" ); +} + +function ControlsMenu::buildFullMapString( %this, %index ) +{ + %name = $RemapName[%index]; + %cmd = $RemapCmd[%index]; + + %temp = moveMap.getBinding( %cmd ); + if ( %temp $= "" ) + return %name TAB ""; + + %mapString = ""; + + %count = getFieldCount( %temp ); + for ( %i = 0; %i < %count; %i += 2 ) + { + if ( %mapString !$= "" ) + %mapString = %mapString @ ", "; + + %device = getField( %temp, %i + 0 ); + %object = getField( %temp, %i + 1 ); + %mapString = %mapString @ %this.getMapDisplayName( %device, %object ); + } + + return %name TAB %mapString; +} + +function ControlsMenu::fillRemapList( %this ) +{ + %remapList = %this-->OptRemapList; + + %remapList.clear(); + for ( %i = 0; %i < $RemapCount; %i++ ) + %remapList.addRow( %i, %this.buildFullMapString( %i ) ); +} + +function ControlsMenu::doRemap( %this ) +{ + %remapList = %this-->OptRemapList; + + %selId = %remapList.getSelectedId(); + %name = $RemapName[%selId]; + + RemapDlg-->OptRemapText.setValue( "Re-bind \"" @ %name @ "\" to..." ); + OptRemapInputCtrl.index = %selId; + Canvas.pushDialog( RemapDlg ); +} + +function ControlsMenuRebindButton::onClick(%this) +{ + %name = $RemapName[%this.keybindIndex]; + RemapDlg-->OptRemapText.setValue( "Re-bind \"" @ %name @ "\" to..." ); + + OptRemapInputCtrl.index = %this.keybindIndex; + OptRemapInputCtrl.optionIndex = %this.optionIndex; + Canvas.pushDialog( RemapDlg ); +} + +function OptRemapInputCtrl::onInputEvent( %this, %device, %action ) +{ + //error( "** onInputEvent called - device = " @ %device @ ", action = " @ %action @ " **" ); + Canvas.popDialog( RemapDlg ); + + // Test for the reserved keystrokes: + if ( %device $= "keyboard" ) + { + // Cancel... + if ( %action $= "escape" ) + { + // Do nothing... + return; + } + } + + %cmd = $RemapCmd[%this.index]; + %name = $RemapName[%this.index]; + + // Grab the friendly display name for this action + // which we'll use when prompting the user below. + %mapName = ControlsMenu.getMapDisplayName( %device, %action ); + + // Get the current command this action is mapped to. + %prevMap = moveMap.getCommand( %device, %action ); + + // If nothing was mapped to the previous command + // mapping then it's easy... just bind it. + if ( %prevMap $= "" ) + { + ControlsMenu.unbindExtraActions( %cmd, 1 ); + moveMap.bind( %device, %action, %cmd ); + + //ControlsMenu.reload(); + %newCommands = getField(ControlsMenu.buildFullMapString( %this.index ), 1); + ControlsMenuOptionsArray.getObject(%this.optionIndex)-->rebindButton.setText(%newCommands); + return; + } + + // If the previous command is the same as the + // current then they hit the same input as what + // was already assigned. + if ( %prevMap $= %cmd ) + { + ControlsMenu.unbindExtraActions( %cmd, 0 ); + moveMap.bind( %device, %action, %cmd ); + + //ControlsMenu.reload(); + %newCommands = getField(ControlsMenu.buildFullMapString( %this.index ), 1); + ControlsMenuOptionsArray.getObject(%this.optionIndex)-->rebindButton.setText(%newCommands); + return; + } + + // Look for the index of the previous mapping. + %prevMapIndex = ControlsMenu.findRemapCmdIndex( %prevMap ); + + // If we get a negative index then the previous + // mapping was to an item that isn't included in + // the mapping list... so we cannot unmap it. + if ( %prevMapIndex == -1 ) + { + MessageBoxOK( "Remap Failed", "\"" @ %mapName @ "\" is already bound to a non-remappable command!" ); + return; + } + + // Setup the forced remapping callback command. + %callback = "redoMapping(" @ %device @ ", \"" @ %action @ "\", \"" @ + %cmd @ "\", " @ %prevMapIndex @ ", " @ %this.index @ ");"; + + // Warn that we're about to remove the old mapping and + // replace it with another. + %prevCmdName = $RemapName[%prevMapIndex]; + Canvas.pushDialog( RemapConfirmDlg ); + + RemapConfirmationText.setText("\"" @ %mapName @ "\" is already bound to \"" + @ %prevCmdName @ "\"! Do you wish to replace this mapping?"); + RemapConfirmationYesButton.command = "ControlsMenu.redoMapping(" @ %device @ ", \"" @ %action @ "\", \"" @ + %cmd @ "\", " @ %prevMapIndex @ ", " @ %this.index @ "); Canvas.popDialog();"; + RemapConfirmationNoButton.command = "Canvas.popDialog();"; + + /*MessageBoxYesNo( "Warning", + "\"" @ %mapName @ "\" is already bound to \"" + @ %prevCmdName @ "\"!\nDo you wish to replace this mapping?", + %callback, "" );*/ +} + +function ControlsMenu::findRemapCmdIndex( %this, %command ) +{ + for ( %i = 0; %i < $RemapCount; %i++ ) + { + if ( %command $= $RemapCmd[%i] ) + return( %i ); + } + return( -1 ); +} + +/// This unbinds actions beyond %count associated to the +/// particular moveMap %commmand. +function ControlsMenu::unbindExtraActions( %this, %command, %count ) +{ + %temp = moveMap.getBinding( %command ); + if ( %temp $= "" ) + return; + + %count = getFieldCount( %temp ) - ( %count * 2 ); + for ( %i = 0; %i < %count; %i += 2 ) + { + %device = getField( %temp, %i + 0 ); + %action = getField( %temp, %i + 1 ); + + moveMap.unbind( %device, %action ); + } +} + +function ControlsMenu::redoMapping( %this, %device, %action, %cmd, %oldIndex, %newIndex ) +{ + //%actionMap.bind( %device, %action, $RemapCmd[%newIndex] ); + moveMap.bind( %device, %action, %cmd ); + + %remapList = %this-->OptRemapList; + %remapList.setRowById( %oldIndex, %this.buildFullMapString( %oldIndex ) ); + %remapList.setRowById( %newIndex, %this.buildFullMapString( %newIndex ) ); + + %this.changeSettingsPage(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/datablocks/guiSounds.cs b/Templates/BaseGame/game/data/ui/scripts/datablocks/guiSounds.cs new file mode 100644 index 000000000..0e8534eec --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/datablocks/guiSounds.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +singleton SFXProfile(menuButtonPressed) +{ + preload = true; + description = AudioGui; + fileName = "data/ui/sound/buttonClick"; +}; + +singleton SFXProfile(menuButtonHover) +{ + preload = true; + description = AudioGui; + fileName = "data/ui/sound/buttonHover"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/graphicsMenu.cs b/Templates/BaseGame/game/data/ui/scripts/graphicsMenu.cs new file mode 100644 index 000000000..7189d11c2 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/graphicsMenu.cs @@ -0,0 +1,567 @@ +// ============================================================================= +// GRAPHICS MENU +// ============================================================================= +//Mesh and Textures +// +function GraphicsMenu::onWake(%this) +{ + DisplaySettingsMenu.hidden = false; + GeneralGraphicsSettingsMenu.hidden = true; + + %this.refresh(); +} + +function GraphicsMenu::refresh(%this) +{ + // + // Display Menu + GraphicsMenuFullScreen.setStateOn( Canvas.isFullScreen() ); + GraphicsMenuVSync.setStateOn( !$pref::Video::disableVerticalSync ); + + %this.initResMenu(); + %resSelId = GraphicsMenuResolution.findText( _makePrettyResString( $pref::Video::mode ) ); + if( %resSelId != -1 ) + GraphicsMenuResolution.setSelected( %resSelId ); + + GraphicsMenuDriver.clear(); + + %buffer = getDisplayDeviceList(); + %count = getFieldCount( %buffer ); + for(%i = 0; %i < %count; %i++) + GraphicsMenuDriver.add(getField(%buffer, %i), %i); + + %selId = GraphicsMenuDriver.findText( getDisplayDeviceInformation() ); + if ( %selId == -1 ) + GraphicsMenuDriver.setFirstSelected(); + else + GraphicsMenuDriver.setSelected( %selId ); + // + + // + // General Graphics menu + GraphicsMenuShadowQlty.init(ShadowQualityList); + GraphicsMenuSoftShadow.init(SoftShadowList); + + GraphicsMenuModelDtl.init(MeshQualityGroup); + GraphicsMenuTextureDtl.init(TextureQualityGroup); + GraphicsMenuTerrainDtl.init(TerrainQualityGroup); + GraphicsMenuDecalLife.init(DecalLifetimeGroup); + GraphicsMenuGroundClutter.init(GroundCoverDensityGroup); + + GraphicsMenuMaterialQlty.init(ShaderQualityGroup); + + // Setup the anisotropic filtering menu. + %ansioCtrl = GraphicsMenuAniso; + %ansioCtrl.clear(); + %ansioCtrl.add( "16X", 16 ); + %ansioCtrl.add( "8X", 8 ); + %ansioCtrl.add( "4X", 4 ); + %ansioCtrl.add( "Off", 0 ); + %ansioCtrl.setSelected( $pref::Video::defaultAnisotropy, false ); + + // set up the Refresh Rate menu. + %refreshMenu = GraphicsMenuRefreshRate; + %refreshMenu.clear(); + // %refreshMenu.add("Auto", 60); + %refreshMenu.add("60", 60); + %refreshMenu.add("75", 75); + %refreshMenu.setSelected( $pref::Video::RefreshRate ); + + // Populate the Anti-aliasing popup. + %aaMenu = GraphicsMenuAA; + %aaMenu.clear(); + %aaMenu.Add( "4x", 4 ); + %aaMenu.Add( "2x", 2 ); + %aaMenu.Add( "1x", 1 ); + %aaMenu.Add( "Off", 0 ); + %aaMenu.setSelected( $pref::Video::AA ); + + //Parallax + GraphicsMenuParallax.setStateOn(!$pref::Video::disableParallaxMapping); + + //water reflections + GraphicsMenuWaterRefl.setStateOn(!$pref::Water::disableTrueReflections); + + GraphicsMenuParallax.setStateOn(!$pref::Video::disableParallaxMapping); + + GraphicsMenuAO.setStateOn($pref::PostFX::EnableSSAO); + GraphicsMenuHDR.setStateOn($pref::PostFX::EnableHDR); + GraphicsMenuDOF.setStateOn($pref::PostFX::EnableDOF); + GraphicsMenuVignette.setStateOn($pref::PostFX::EnableVignette); + GraphicsMenuLightRay.setStateOn($pref::PostFX::EnableLightRays); +} + +function GraphicsMenuOKButton::onClick(%this) +{ + //save the settings and then back out + GraphicsMenu.apply(); + OptionsMenu.backOut(); +} + +function GraphicsMenu::initResMenu( %this ) +{ + // Clear out previous values + %resMenu = GraphicsMenuResolution; + %resMenu.clear(); + + // If we are in a browser then we can't change our resolution through + // the options dialog + if (getWebDeployment()) + { + %count = 0; + %currRes = getWords(Canvas.getVideoMode(), $WORD::RES_X, $WORD::RES_Y); + %resMenu.add(%currRes, %count); + %count++; + + return; + } + + // Loop through all and add all valid resolutions + %count = 0; + %resCount = Canvas.getModeCount(); + for (%i = 0; %i < %resCount; %i++) + { + %testResString = Canvas.getMode( %i ); + %testRes = _makePrettyResString( %testResString ); + + // Only add to list if it isn't there already. + if (%resMenu.findText(%testRes) == -1) + { + %resMenu.add(%testRes, %i); + %count++; + } + } + + %resMenu.sort(); +} + +function GraphicsQualityPopup::init( %this, %qualityGroup ) +{ + assert( isObject( %this ) ); + assert( isObject( %qualityGroup ) ); + + %this.qualityGroup = %qualityGroup; + + // Clear the existing content first. + %this.clear(); + + // Fill it. + %select = -1; + for ( %i=0; %i < %qualityGroup.getCount(); %i++ ) + { + %level = %qualityGroup.getObject( %i ); + if ( %level.isCurrent() ) + %select = %i; + + %this.add( %level.displayName, %i ); + } + + // Setup a default selection. + if ( %select == -1 ) + %this.setText( "Custom" ); + else + %this.setSelected( %select ); +} + +function GraphicsQualityPopup::apply( %this ) +{ + %levelName = %this.getText(); + %this.qualityGroup.applySetting(%levelName); +} +// +function GraphicsMenu::Autodetect(%this) +{ + $pref::Video::autoDetect = false; + + %shaderVer = getPixelShaderVersion(); + %intel = ( strstr( strupr( getDisplayDeviceInformation() ), "INTEL" ) != -1 ) ? true : false; + %videoMem = GFXCardProfilerAPI::getVideoMemoryMB(); + + return %this.Autodetect_Apply( %shaderVer, %intel, %videoMem ); +} + +function GraphicsMenu::Autodetect_Apply(%this, %shaderVer, %intel, %videoMem ) +{ + if ( %shaderVer < 2.0 ) + { + return "Your video card does not meet the minimum requirment of shader model 2.0."; + } + + if ( %shaderVer < 3.0 || %intel ) + { + // Allow specular and normals for 2.0a and 2.0b + if ( %shaderVer > 2.0 ) + { + MeshQualityGroup.applySetting("Lowest"); + TextureQualityGroup.applySetting("Lowest"); + GroundCoverDensityGroup.applySetting("Lowest"); + DecalLifetimeGroup.applySetting("None"); + TerrainQualityGroup.applySetting("Lowest"); + ShaderQualityGroup.applySetting("High"); + + ShadowQualityList.applySetting("None"); + + SoftShadowList.applySetting("Off"); + + $pref::Shadows::useShadowCaching = true; + + $pref::Water::disableTrueReflections = true; + $pref::Video::disableParallaxMapping = true; + $pref::PostFX::EnableSSAO = false; + $pref::PostFX::EnableHDR = false; + $pref::PostFX::EnableDOF = false; + $pref::PostFX::EnableLightRays = false; + $pref::PostFX::EnableVignette = false; + + $pref::Video::AA = 0; + $pref::Video::disableVerticalSync = 0; + } + else + { + MeshQualityGroup.applySetting("Lowest"); + TextureQualityGroup.applySetting("Lowest"); + GroundCoverDensityGroup.applySetting("Lowest"); + DecalLifetimeGroup.applySetting("None"); + TerrainQualityGroup.applySetting("Lowest"); + ShaderQualityGroup.applySetting("Low"); + + ShadowQualityList.applySetting("None"); + + SoftShadowList.applySetting("Off"); + + $pref::Shadows::useShadowCaching = true; + + $pref::Water::disableTrueReflections = true; + $pref::Video::disableParallaxMapping = true; + $pref::PostFX::EnableSSAO = false; + $pref::PostFX::EnableHDR = false; + $pref::PostFX::EnableDOF = false; + $pref::PostFX::EnableLightRays = false; + $pref::PostFX::EnableVignette = false; + + $pref::Video::AA = 0; + $pref::Video::disableVerticalSync = 0; + } + } + else + { + if ( %videoMem > 1000 ) + { + MeshQualityGroup.applySetting("High"); + TextureQualityGroup.applySetting("High"); + GroundCoverDensityGroup.applySetting("High"); + DecalLifetimeGroup.applySetting("High"); + TerrainQualityGroup.applySetting("High"); + ShaderQualityGroup.applySetting("High"); + + ShadowQualityList.applySetting("High"); + + SoftShadowList.applySetting("High"); + + //Should this default to on in ultra settings? + $pref::Shadows::useShadowCaching = true; + + $pref::Water::disableTrueReflections = false; + $pref::Video::disableParallaxMapping = false; + $pref::PostFX::EnableSSAO = true; + $pref::PostFX::EnableHDR = true; + $pref::PostFX::EnableDOF = true; + $pref::PostFX::EnableLightRays = true; + $pref::PostFX::EnableVignette = true; + + $pref::Video::AA = 4; + $pref::Video::disableVerticalSync = 16; + } + else if ( %videoMem > 400 || %videoMem == 0 ) + { + MeshQualityGroup.applySetting("Medium"); + TextureQualityGroup.applySetting("Medium"); + GroundCoverDensityGroup.applySetting("Medium"); + DecalLifetimeGroup.applySetting("Medium"); + TerrainQualityGroup.applySetting("Medium"); + ShaderQualityGroup.applySetting("High"); + + ShadowQualityList.applySetting("Medium"); + + SoftShadowList.applySetting("Low"); + + $pref::Shadows::useShadowCaching = true; + + $pref::Water::disableTrueReflections = false; + $pref::Video::disableParallaxMapping = true; + $pref::PostFX::EnableSSAO = false; + $pref::PostFX::EnableHDR = true; + $pref::PostFX::EnableDOF = true; + $pref::PostFX::EnableLightRays = true; + $pref::PostFX::EnableVignette = true; + + $pref::Video::AA = 4; + $pref::Video::disableVerticalSync = 4; + + if ( %videoMem == 0 ) + return "Torque was unable to detect available video memory. Applying 'Medium' quality."; + } + else + { + MeshQualityGroup.applySetting("Low"); + TextureQualityGroup.applySetting("Low"); + GroundCoverDensityGroup.applySetting("Low"); + DecalLifetimeGroup.applySetting("Low"); + TerrainQualityGroup.applySetting("Low"); + ShaderQualityGroup.applySetting("Low"); + + ShadowQualityList.applySetting("None"); + + SoftShadowList.applySetting("Off"); + + $pref::Shadows::useShadowCaching = true; + + $pref::Water::disableTrueReflections = false; + $pref::Video::disableParallaxMapping = true; + $pref::PostFX::EnableSSAO = false; + $pref::PostFX::EnableHDR = false; + $pref::PostFX::EnableDOF = false; + $pref::PostFX::EnableLightRays = false; + $pref::PostFX::EnableVignette = false; + + $pref::Video::AA = 0; + $pref::Video::disableVerticalSync = 0; + } + } + + %this.refresh(); + + %this.apply(); + + //force postFX updates + PostFXManager.settingsEffectSetEnabled("SSAO", $pref::PostFX::EnableSSAO); + PostFXManager.settingsEffectSetEnabled("HDR", $pref::PostFX::EnableHDR); + PostFXManager.settingsEffectSetEnabled("DOF", $pref::PostFX::EnableDOF); + PostFXManager.settingsEffectSetEnabled("LightRays", $pref::PostFX::EnableLightRays); + PostFXManager.settingsEffectSetEnabled("Vignette", $pref::PostFX::EnableVignette); + + return "Graphics quality settings have been auto detected."; +} + +function _makePrettyResString( %resString ) +{ + %width = getWord( %resString, $WORD::RES_X ); + %height = getWord( %resString, $WORD::RES_Y ); + + %aspect = %width / %height; + %aspect = mRound( %aspect * 100 ) * 0.01; + + switch$( %aspect ) + { + case "1.33": + %aspect = "4:3"; + case "1.78": + %aspect = "16:9"; + default: + %aspect = ""; + } + + %outRes = %width @ " x " @ %height; + if ( %aspect !$= "" ) + %outRes = %outRes @ " (" @ %aspect @ ")"; + + return %outRes; +} + +// +function GraphicsMenuSetting::init( %this ) +{ + assert( isObject( %this ) ); + assert( isObject( %this.qualitySettingGroup ) ); + + // Fill it. + %select = -1; + %selectedName = ""; + for ( %i=0; %i < %this.qualitySettingGroup.getCount(); %i++ ) + { + %level = %this.qualitySettingGroup.getObject( %i ); + + %levelName = %level.displayName; + if ( %level.isCurrent() ) + { + %select = %i; + %selectedName = %level.displayName; + } + } + + // Setup a default selection. + if ( %select == -1 ) + { + %this-->SettingText.setText( "Custom" ); + %this.selectedLevel = %this.qualitySettingGroup.getCount(); + } + else + { + %this-->SettingText.setText(%selectedName); + %this.selectedLevel = %select; + } +} + +function GraphicsQualityLevel::isCurrent( %this ) +{ + // Test each pref to see if the current value + // equals our stored value. + + for ( %i=0; %i < %this.count(); %i++ ) + { + %pref = %this.getKey( %i ); + %value = %this.getValue( %i ); + + if ( getVariable( %pref ) !$= %value ) + return false; + } + + return true; +} + +function GraphicsQualityLevel::apply( %this ) +{ + for ( %i=0; %i < %this.count(); %i++ ) + { + %pref = %this.getKey( %i ); + %value = %this.getValue( %i ); + setVariable( %pref, %value ); + } + + // If we have an overloaded onApply method then + // call it now to finalize the changes. + if ( %this.isMethod( "onApply" ) ) + %this.onApply(); + else + { + %group = %this.getGroup(); + if ( isObject( %group ) && %group.isMethod( "onApply" ) ) + %group.onApply( %this ); + } +} + +function GraphicsOptionsMenuGroup::applySetting(%this, %settingName) +{ + for(%i=0; %i < %this.getCount(); %i++) + { + %setting = %this.getObject(%i); + if(%setting.displayName $= %settingName) + { + for ( %s=0; %s < %setting.count(); %s++ ) + { + %pref = %setting.getKey( %s ); + %value = %setting.getValue( %s ); + setVariable( %pref, %value ); + } + } + } +} + +function GraphicsMenu::apply(%this) +{ + %newAdapter = GraphicsMenuDriver.getText(); + %numAdapters = GFXInit::getAdapterCount(); + %newDevice = $pref::Video::displayDevice; + + for( %i = 0; %i < %numAdapters; %i ++ ) + { + if( GFXInit::getAdapterName( %i ) $= %newAdapter ) + { + %newDevice = GFXInit::getAdapterType( %i ); + break; + } + } + + // Change the device. + if ( %newDevice !$= $pref::Video::displayDevice ) + { + if ( %testNeedApply ) + return true; + + $pref::Video::displayDevice = %newDevice; + if( %newAdapter !$= getDisplayDeviceInformation() ) + MessageBoxOK( "Change requires restart", "Please restart the game for a display device change to take effect." ); + } + + GraphicsMenuShadowQlty.apply(); + GraphicsMenuSoftShadow.apply(); + + GraphicsMenuModelDtl.apply(); + GraphicsMenuTextureDtl.apply(); + GraphicsMenuTerrainDtl.apply(); + GraphicsMenuDecalLife.apply(); + GraphicsMenuGroundClutter.apply(); + + GraphicsMenuMaterialQlty.apply(); + + //Update Textures + reloadTextures(); + + //Update lighting + // Set the light manager. This should do nothing + // if its already set or if its not compatible. + setLightManager( $pref::lightManager ); + + PostFXManager.settingsEffectSetEnabled("SSAO", $pref::PostFX::EnableSSAO); + PostFXManager.settingsEffectSetEnabled("HDR", $pref::PostFX::EnableHDR); + PostFXManager.settingsEffectSetEnabled("DOF", $pref::PostFX::EnableDOF); + PostFXManager.settingsEffectSetEnabled("LightRays", $pref::PostFX::EnableLightRays); + PostFXManager.settingsEffectSetEnabled("Vignette", $pref::PostFX::EnableVignette); + + $pref::Video::disableParallaxMapping = !GraphicsMenuParallax.isStateOn(); + + //water reflections + $pref::Water::disableTrueReflections = !GraphicsMenuWaterRefl.isStateOn(); + + //Update the display settings now + $pref::Video::Resolution = getWords( Canvas.getMode( GraphicsMenuResolution.getSelected() ), $WORD::RES_X, $WORD::RES_Y ); + %newBpp = 32; // ... its not 1997 anymore. + $pref::Video::FullScreen = GraphicsMenuFullScreen.isStateOn() ? "true" : "false"; + $pref::Video::RefreshRate = GraphicsMenuRefreshRate.getSelected(); + $pref::Video::disableVerticalSync = !GraphicsMenuVSync.isStateOn(); + $pref::Video::AA = GraphicsMenuAA.getSelected(); + + if ( %newFullScreen $= "false" ) + { + // If we're in windowed mode switch the fullscreen check + // if the resolution is bigger than the desktop. + %deskRes = getDesktopResolution(); + %deskResX = getWord(%deskRes, $WORD::RES_X); + %deskResY = getWord(%deskRes, $WORD::RES_Y); + if ( getWord( %newRes, $WORD::RES_X ) > %deskResX || + getWord( %newRes, $WORD::RES_Y ) > %deskResY ) + { + $pref::Video::FullScreen = "true"; + GraphicsMenuFullScreen.setStateOn( true ); + } + } + + // Build the final mode string. + %newMode = $pref::Video::Resolution SPC $pref::Video::FullScreen SPC %newBpp SPC $pref::Video::RefreshRate SPC $pref::Video::AA; + + // Change the video mode. + if ( %newMode !$= $pref::Video::mode || + %newVsync != $pref::Video::disableVerticalSync ) + { + if ( %testNeedApply ) + return true; + + $pref::Video::mode = %newMode; + $pref::Video::disableVerticalSync = %newVsync; + configureCanvas(); + } + + // Check the anisotropic filtering. + %level = GraphicsMenuAniso.getSelected(); + if ( %level != $pref::Video::defaultAnisotropy ) + { + if ( %testNeedApply ) + return true; + + $pref::Video::defaultAnisotropy = %level; + } + + echo("Exporting client prefs"); + %prefPath = getPrefpath(); + export("$pref::*", %prefPath @ "/clientPrefs.cs", false); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/RecordingsDlg.gui b/Templates/BaseGame/game/data/ui/scripts/guis/RecordingsDlg.gui new file mode 100644 index 000000000..0e863fff5 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/RecordingsDlg.gui @@ -0,0 +1,230 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(recordingsDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + helpTag = "0"; + + new GuiWindowCtrl() { + text = "Demo Recordings"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "Canvas.popDialog(recordingsDlg);"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "247 215"; + extent = "530 338"; + minExtent = "48 92"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "23 60"; + extent = "484 237"; + minExtent = "32 32"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl(RecordingsDlgList) { + columns = "0"; + fitParentWidth = "1"; + clipColumnText = "0"; + position = "1 1"; + extent = "469 32"; + minExtent = "8 20"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextArrayProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl(DR_CancelBtn) { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "396 306"; + extent = "110 20"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(recordingsDlg);"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(DR_StartDemoBtn) { + text = "Play"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "25 305"; + extent = "110 20"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "StartSelectedDemo();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "During gameplay press the following keys:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "23 30"; + extent = "206 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Start = F3"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "253 32"; + extent = "50 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Stop = F4"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "320 32"; + extent = "49 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(DR_DelDemoBtn) { + text = "Delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "210 305"; + extent = "110 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "deleteDemoRecord();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/chooseLevelDlg.gui b/Templates/BaseGame/game/data/ui/scripts/guis/chooseLevelDlg.gui new file mode 100644 index 000000000..d6dec3e48 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/chooseLevelDlg.gui @@ -0,0 +1,287 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ChooseLevelDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + launchInEditor = "0"; + returnGui = "MainMenuGui"; + + new GuiControl(ChooseLevelWindow) { + position = "80 36"; + extent = "770 616"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "./art/no-preview"; + wrap = "0"; + position = "369 31"; + extent = "400 300"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "CurrentPreview"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + }; + new GuiTextCtrl() { + text = "Empty Room"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "370 335"; + extent = "90 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "levelName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Description:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "370 354"; + extent = "91 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiMLTextCtrl() { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + position = "370 380"; + extent = "165 28"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "LevelDescription"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "./art/previous-button"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "2 -1"; + extent = "368 33"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + command = "ChooseLevelWindow.previousPreviews();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "PreviousSmallPreviews"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "./art/next-button"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "-3 549"; + extent = "374 33"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + command = "ChooseLevelWindow.nextPreviews();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "NextSmallPreviews"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + wrap = "0"; + }; + new GuiTextListCtrl(CL_levelList) { + columns = "0"; + fitParentWidth = "1"; + clipColumnText = "0"; + position = "-7 1"; + extent = "80 30"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextArrayProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiDynamicCtrlArrayControl() { + colCount = "1"; + colSize = "368"; + rowCount = "14"; + rowSize = "35"; + rowSpacing = "0"; + colSpacing = "10"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "0"; + dynamicSize = "0"; + padding = "0 0 0 0"; + position = "2 33"; + extent = "368 516"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "SmallPreviews"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Empty"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "0 0"; + extent = "368 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "ChooseLevelWindow.previewSelected(ChooseLevelWindow->SmallPreviews->SmallPreview0);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "SmallPreview0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl(ChooseLevelDlgGoBtn) { + text = "Start Level"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "371 583"; + extent = "399 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ChooseLevelDlgBackBtn) { + text = "Return to Menu"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 583"; + extent = "371 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(ChooseLevelDlg);\n\nif(isObject(ChooseLevelDlg.returnGui) && ChooseLevelDlg.returnGui.isMethod(\"onReturnTo\")) ChooseLevelDlg.returnGui.onReturnTo();"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/controlsMenuSetting.taml b/Templates/BaseGame/game/data/ui/scripts/guis/controlsMenuSetting.taml new file mode 100644 index 000000000..f0da59eed --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/controlsMenuSetting.taml @@ -0,0 +1,102 @@ + + + + + + + diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsCtrl.taml b/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsCtrl.taml new file mode 100644 index 000000000..b5beaf713 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsCtrl.taml @@ -0,0 +1,159 @@ + + + + + + + + + + diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsSlider.taml b/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsSlider.taml new file mode 100644 index 000000000..4bbc1c982 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/graphicsMenuSettingsSlider.taml @@ -0,0 +1,140 @@ + + + + + + + + + diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/joinServerMenu.gui b/Templates/BaseGame/game/data/ui/scripts/guis/joinServerMenu.gui new file mode 100644 index 000000000..96545c438 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/joinServerMenu.gui @@ -0,0 +1,455 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(JoinServerMenu) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiControl(JoinServerWindow) { + position = "80 36"; + extent = "800 616"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(JS_status) { + text = "No servers found."; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "277 31"; + extent = "148 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 80"; + extent = "780 461"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl(JS_serverList) { + columns = "0 200 270 335 400"; + fitParentWidth = "1"; + clipColumnText = "0"; + position = "1 1"; + extent = "762 8"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextArrayProfile"; + visible = "1"; + active = "1"; + altCommand = "JoinServerDlg.join();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "116 31"; + extent = "144 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Player::Name"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Player Name:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 31"; + extent = "98 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Players"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "269 59"; + extent = "36 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Version"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "335 59"; + extent = "38 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Game"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "412 59"; + extent = "28 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Ping"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "212 59"; + extent = "20 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Server Name"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 59"; + extent = "63 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLWhiteTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl(JS_queryStatus) { + position = "10 541"; + extent = "778 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiProgressCtrl(JS_statusBar) { + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "84 0"; + extent = "695 35"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiProgressProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(JS_cancelQuery) { + text = "Cancel!"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "0 0"; + extent = "84 35"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "JoinServerDlg.cancel();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(JS_statusText) { + text = "Querying master server"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "84 0"; + extent = "695 35"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl(JoinServerBackBtn) { + text = "Return to Menu"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 583"; + extent = "160 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(JoinServerMenu);\n\nif(isObject(JoinServerMenu.returnGui) && JoinServerMenu.returnGui.isMethod(\"onReturnTo\")) JoinServerMenu.returnGui.onReturnTo();"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(JoinServerQryLanBtn) { + text = "Query Lan"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "160 583"; + extent = "160 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "JoinServerMenu.queryLan();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(JoinServerQryInternetBtn) { + text = "Query Internet"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "320 583"; + extent = "160 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "JoinServerMenu.query();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(JoinServerRefreshBtn) { + text = "Refresh Server"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "480 583"; + extent = "160 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "JoinServerMenu.refresh();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(JoinServerJoinBtn) { + text = "Join Server"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "640 583"; + extent = "160 33"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "0"; + command = "JoinServerMenu.join();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/loadingGui.gui b/Templates/BaseGame/game/data/ui/scripts/guis/loadingGui.gui new file mode 100644 index 000000000..d51d9d94c --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/loadingGui.gui @@ -0,0 +1,102 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiChunkedBitmapCtrl(LoadingGui) { + bitmap = "data/ui/art/background-dark.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "1600 838"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + + new GuiControl() { + position = "391 429"; + extent = "497 166"; + minExtent = "8 8"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "disconnect();\nCanvas.setContent(MainMenuGui);"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl(LoadingLogo) { + bitmap = "data/ui/art/Torque-3D-logo.png"; + wrap = "0"; + position = "27 6"; + extent = "443 139"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiProgressBitmapCtrl(LoadingProgress) { + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "17 126"; + extent = "464 24"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "bottom"; + profile = "GuiProgressBitmapProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(LoadingProgressTxt) { + text = "LOADING DATABLOCKS"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "28 144"; + extent = "440 20"; + minExtent = "8 8"; + horizSizing = "center"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/mainMenu.gui b/Templates/BaseGame/game/data/ui/scripts/guis/mainMenu.gui new file mode 100644 index 000000000..a757274ed --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/mainMenu.gui @@ -0,0 +1,194 @@ +exec( "tools/gui/profiles.ed.cs" ); + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiChunkedBitmapCtrl(MainMenuGui) { + bitmap = "data/ui/art/background-dark.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + Enabled = "1"; + isDecoy = "0"; + + new GuiBitmapButtonCtrl(MainMenuAppLogo) { + bitmap = "data/ui/art/Torque-3D-logo-shortcut.png"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "368 30"; + extent = "443 139"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "gotoWebPage(\"forums.torque3d.org\");"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + + }; + new GuiControl(MainMenuButtonContainer) { + position = "67 321"; + extent = "442 381"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiDynamicCtrlArrayControl() { + colCount = "1"; + colSize = "442"; + rowCount = "9"; + rowSize = "40"; + rowSpacing = "0"; + colSpacing = "0"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "0"; + dynamicSize = "0"; + padding = "0 0 0 0"; + position = "0 0"; + extent = "442 381"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Singleplayer"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "442 40"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiBlankMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "MainMenuGui.openSinglePlayerMenu();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Create Server"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "0 40"; + extent = "442 40"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiBlankMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "MainMenuGui.openMultiPlayerMenu();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Join Server"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "0 80"; + extent = "442 40"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiBlankMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.pushDialog(JoinServerMenu);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Options"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "0 120"; + extent = "442 40"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiBlankMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "MainMenuGui.openOptionsMenu();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Exit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "0 160"; + extent = "442 40"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiBlankMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "quit();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ExitButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxOK.ed.gui b/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxOK.ed.gui new file mode 100644 index 000000000..52e119ea6 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxOK.ed.gui @@ -0,0 +1,60 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxOKDlg) { + profile = "GuiOverlayProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MBOKFrame) { + profile = "GuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 107"; + minExtent = "48 95"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + + new GuiMLTextCtrl(MBOKText) { + profile = "GuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "9 35"; + extent = "281 24"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "111 75"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKDlg,MessageBoxOKDlg.callback);"; + accelerator = "return"; + helpTag = "0"; + text = "Ok"; + simpleStyle = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxYesNo.gui b/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxYesNo.gui new file mode 100644 index 000000000..b58aa170b --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/messageBoxYesNo.gui @@ -0,0 +1,126 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxYesNoDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiOverlayProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + helpTag = "0"; + + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "168 352"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiChunkedBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.noCallback);"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(MBYesNoText) { + text = "Re-bind \"\" to..."; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "177 8"; + extent = "384 20"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Yes"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "270 36"; + extent = "80 22"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.yesCallback);"; + accelerator = "return"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "No"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "367 36"; + extent = "80 22"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.noCallback);"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/netGraphGui.gui b/Templates/BaseGame/game/data/ui/scripts/guis/netGraphGui.gui new file mode 100644 index 000000000..b034a447e --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/netGraphGui.gui @@ -0,0 +1,557 @@ +function execNetGraphGuiGUI() +{ + if ( isObject( NetGraphGui ) ) + NetGraphGui.delete(); + + if ( isObject( NetGraphProfile ) ) + NetGraphProfile.delete(); + + if ( isObject( NetGraphGhostsActiveProfile ) ) + NetGraphGhostsActiveProfile.delete(); + + if ( isObject( NetGraphGhostUpdatesProfile ) ) + NetGraphGhostUpdatesProfile.delete(); + + if ( isObject( NetGraphBitsSentProfile ) ) + NetGraphBitsSentProfile.delete(); + + if ( isObject( NetGraphBitsReceivedProfile ) ) + NetGraphBitsReceivedProfile.delete(); + + if ( isObject( NetGraphLatencyProfile ) ) + NetGraphLatencyProfile.delete(); + + if ( isObject( NetGraphPacketLossProfile ) ) + NetGraphPacketLossProfile.delete(); + + exec( "./NetGraphGui.gui" ); +} + +// Profiles +new GuiControlProfile (NetGraphProfile) +{ + modal = false; + opaque = false; + canKeyFocus = false; +}; + +new GuiControlProfile (NetGraphKeyContainerProfile) +{ + border = true; + opaque = true; + fillColor = "100 100 100 200"; +}; +new GuiControlProfile (NetGraphGhostsActiveProfile) +{ + border = false; + fontColor = "255 255 255"; +}; +new GuiControlProfile (NetGraphGhostUpdatesProfile) +{ + border = false; + fontColor = "255 0 0"; +}; +new GuiControlProfile (NetGraphBitsSentProfile) +{ + border = false; + fontColor = "0 255 0"; +}; +new GuiControlProfile (NetGraphBitsReceivedProfile) +{ + border = false; + fontColor = "0 0 255"; +}; +new GuiControlProfile (NetGraphLatencyProfile) +{ + border = false; + fontColor = "0 255 255"; +}; +new GuiControlProfile (NetGraphPacketLossProfile) +{ + border = false; + fontColor = "0 0 0"; +}; + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(NetGraphGui) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + noCursor = "1"; + + new GuiGraphCtrl(NetGraph) { + centerY = "1"; + plotColor[0] = "1 1 1 1"; + plotColor[1] = "1 0 0 1"; + plotColor[2] = "0 1 0 1"; + plotColor[3] = "0 0 1 1"; + plotColor[4] = "0 1 1 1"; + plotColor[5] = "0 0 0 1"; + plotType[0] = "PolyLine"; + plotType[1] = "PolyLine"; + plotType[2] = "PolyLine"; + plotType[3] = "PolyLine"; + plotType[4] = "PolyLine"; + plotType[5] = "PolyLine"; + plotInterval[0] = "0"; + plotInterval[1] = "0"; + plotInterval[2] = "0"; + plotInterval[3] = "0"; + plotInterval[4] = "0"; + plotInterval[5] = "0"; + position = "816 5"; + extent = "200 200"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphKeyContainerProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "816 205"; + extent = "200 104"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphKeyContainerProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(GhostsActive) { + text = "Ghosts Active"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphGhostsActiveProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(GhostUpdates) { + text = "Ghost Updates"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "100 0"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphGhostUpdatesProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(BitsSent) { + text = "Bytes Sent"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 18"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphBitsSentProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(BitsReceived) { + text = "Bytes Received"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "100 18"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphBitsReceivedProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(Latency) { + text = "Latency"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 36"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphLatencyProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(PacketLoss) { + text = "Packet Loss"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "100 36"; + extent = "59 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Network Simulation:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 52"; + extent = "97 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Simulated Latency:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 68"; + extent = "91 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "ms"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "179 68"; + extent = "20 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(NetGraphSimLatency) { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "0"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "112 67"; + extent = "64 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Simulated Packet Loss:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 83"; + extent = "111 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "179 84"; + extent = "20 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "NetGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(NetGraphSimPacket) { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "0"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "112 85"; + extent = "64 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + command = "if(NetGraphSimLatency.text $= \"\" || NetGraphSimLatency.text < 0)\n{\n NetGraphSimLatency.text = 0;\n}\n\nif(NetGraphSimPacket.text $= \"\" || NetGraphSimPacket.text < 0)\n{\n NetGraphSimLatency.text = 0;\n}\nelse if(NetGraphSimPacket.text > 100)\n{\n NetGraphSimPacket.text = 100;\n}\n\nnetSimulateLag( NetGraphSimLatency.text, NetGraphSimPacket.text );"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +// Functions +function toggleNetGraph() +{ + if(!$NetGraph::isInitialized) + { + NetGraph::updateStats(); + $NetGraph::isInitialized = true; + } + + if(!Canvas.isMember(NetGraphGui)) + { + Canvas.add(NetGraphGui); + } + else + { + Canvas.remove(NetGraphGui); + netSimulateLag( 0, 0 ); + } +} + +function NetGraph::updateStats() +{ + $NetGraphThread = NetGraph.schedule(32, "updateStats"); + + if(!$Stats::netGhostUpdates) + return; + + if(isobject(NetGraph)) + { + if(isobject(ServerConnection)) + NetGraph.addDatum(0,ServerConnection.getGhostsActive()); + GhostsActive.setText("Ghosts Active: " @ ServerConnection.getGhostsActive()); + NetGraph.addDatum(1,$Stats::netGhostUpdates); + GhostUpdates.setText("Ghost Updates: " @ $Stats::netGhostUpdates); + NetGraph.addDatum(2,$Stats::netBitsSent); + BitsSent.setText("Bytes Sent: " @ $Stats::netBitsSent); + NetGraph.addDatum(3,$Stats::netBitsReceived); + BitsReceived.setText("Bytes Received: " @ $Stats::netBitsReceived); + NetGraph.matchScale(2,3); + NetGraph.addDatum(4,ServerConnection.getPing()); + Latency.setText("Latency: " @ ServerConnection.getPing()); + NetGraph.addDatum(5,ServerConnection.getPacketLoss()); + PacketLoss.setText("Packet Loss: " @ ServerConnection.getPacketLoss()); + } +} + +function NetGraph::toggleKey() +{ + if(!GhostsActive.visible) + { + GhostsActive.visible = 1; + GhostUpdates.visible = 1; + BitsSent.visible = 1; + BitsReceived.visible = 1; + Latency.visible = 1; + PacketLoss.visible = 1; + } + else + { + GhostsActive.visible = 0; + GhostUpdates.visible = 0; + BitsSent.visible = 0; + BitsReceived.visible = 0; + Latency.visible = 0; + PacketLoss.visible = 0; + } +} + +function NetGraphSimLatency::onReturn(%this) +{ + NetGraph.updateNetworkSimulation(); +} + +function NetGraphSimPacket::onReturn(%this) +{ + NetGraph.updateNetworkSimulation(); +} + +function NetGraph::updateNetworkSimulation(%this) +{ + %latency = NetGraphSimLatency.getText(); + + if(%latency $= "" || %latency < 0) + { + NetGraphSimLatency.text = 0; + %latency = 0; + } + + %packetLoss = NetGraphSimPacket.getText(); + + if(%packetLoss $= "" || %packetLoss < 0) + { + NetGraphSimLatency.text = 0; + %packetLoss = 0; + } + else if(%packetLoss > 100) + { + NetGraphSimPacket.text = 100; + %packetLoss = 100; + } + + netSimulateLag( %latency, %packetLoss ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/optionsDlg.gui b/Templates/BaseGame/game/data/ui/scripts/guis/optionsDlg.gui new file mode 100644 index 000000000..fcff1cb95 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/optionsDlg.gui @@ -0,0 +1,1417 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(OptionsDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiOverlayProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + fixedAspectRatio = "0"; + + new GuiWindowCtrl() { + text = "Options"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "Canvas.popDialog(optionsDlg);"; + edgeSnap = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "323 232"; + extent = "377 303"; + minExtent = "8 8"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Done"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "306 271"; + extent = "60 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(optionsDlg);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapBorderCtrl() { + position = "9 55"; + extent = "358 210"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptControlsPane"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 24"; + extent = "347 152"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl() { + columns = "0 160"; + fitParentWidth = "1"; + clipColumnText = "0"; + position = "1 1"; + extent = "329 780"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + altCommand = "OptionsDlg.doRemap();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptRemapList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Control Name"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 6"; + extent = "64 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Control Binding"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "165 6"; + extent = "72 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiSliderCtrl(OptMouseSensitivity) { + range = "0.02 2"; + ticks = "10"; + value = "0.75"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "105 182"; + Extent = "244 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "OptMouseSetSensitivity(OptMouseSensitivity.value);"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Mouse Sensitivity:"; + maxLength = "255"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "15 182"; + Extent = "85 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + position = "9 55"; + extent = "358 210"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptAudioPane"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiMLTextCtrl() { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + position = "149 10"; + extent = "190 14"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMLTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAudioInfo"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Audio Provider:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "16 16"; + extent = "75 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(OptAudioProviderList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Null"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "101 15"; + extent = "240 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Audio Device:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "23 48"; + extent = "75 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(OptAudioDeviceList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "SFX Null Device"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "101 47"; + extent = "240 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "18 84"; + extent = "325 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Master Volume"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "72 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "0.8"; + position = "85 1"; + extent = "240 14"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + altCommand = "OptAudioUpdateMasterVolume( $thisControl.value );"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAudioVolumeMaster"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "9 115"; + extent = "334 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Interface Volume"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "82 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "1"; + position = "94 2"; + extent = "240 13"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + altCommand = "OptAudioUpdateChannelVolume(AudioGui, $thisControl.value);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAudioVolumeShell"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "18 146"; + extent = "325 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Effects Volume"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "74 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "1"; + position = "85 2"; + extent = "240 13"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + altCommand = "OptAudioUpdateChannelVolume(AudioEffect, $thisControl.value);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAudioVolumeSim"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "23 177"; + extent = "320 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl() { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "1"; + position = "80 2"; + extent = "240 13"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + altCommand = "OptAudioUpdateChannelVolume(AudioMusic, $thisControl.value);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAudioVolumeMusic"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Music Volume"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "67 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiBitmapBorderCtrl() { + position = "9 55"; + extent = "358 210"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptGraphicsPane"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Display Driver:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 8"; + extent = "70 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Resolution:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 35"; + extent = "53 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Fullscreen"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "11 62"; + extent = "85 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptGraphicsFullscreenToggle"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(OptGraphicsDriverMenu) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "ATI Radeon HD 5700 Series (D3D9)"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "88 8"; + extent = "258 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "1024 x 768 (4:3)"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "67 35"; + extent = "127 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptGraphicsResolutionMenu"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Refresh:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "207 35"; + extent = "45 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "60"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "252 35"; + extent = "49 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptRefreshSelectMenu"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Mesh Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "21 91"; + extent = "62 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Low"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "90 91"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptMeshQualityPopup"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Low"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "90 118"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptTextureQualityPopup"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Texture Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 118"; + extent = "77 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Low"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "90 143"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptLightingQualityPopup"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Lighting Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 143"; + extent = "73 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Effect Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "191 91"; + extent = "73 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "263 91"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptEffectQualityPopup"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Shader Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "186 118"; + extent = "77 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Low"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "263 118"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptShaderQualityPopup"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Particle Quality:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "186 156"; + extent = "73 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "263 156"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "0"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptParticleQualityPopup"; + class = "GraphicsQualityPopup"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Anisotropic Filtering:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "22 167"; + extent = "105 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Off"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "123 167"; + extent = "45 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAnisotropicPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Vertical Sync"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "92 62"; + extent = "85 18"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptGraphicsVSyncToggle"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Auto Detect Quality"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "205 152"; + extent = "110 27"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._autoDetectQuality();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "263 62"; + extent = "78 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "OptionsDlg._updateApplyState();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptAAQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Anti-aliasing"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "191 62"; + extent = "73 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "0 190"; + extent = "352 15"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "GammaSliderContainer"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl() { + range = "0.001 2.2"; + ticks = "0"; + snap = "0"; + value = "1"; + position = "76 -1"; + extent = "268 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Video::Gamma"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Gamma:"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "22 -4"; + extent = "105 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + position = "9 55"; + extent = "357 208"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiWindowProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptNetworkPane"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Graphics"; + groupNum = "-1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "9 33"; + extent = "117 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonTabProfile"; + visible = "1"; + active = "1"; + command = "optionsDlg.setPane(Graphics);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "OptGraphicsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Audio"; + groupNum = "-1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "126 33"; + extent = "117 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonTabProfile"; + visible = "1"; + active = "1"; + command = "optionsDlg.setPane(Audio);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Controls"; + groupNum = "-1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "243 33"; + extent = "117 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonTabProfile"; + visible = "1"; + active = "1"; + command = "optionsDlg.setPane(Controls);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Apply"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "241 271"; + extent = "60 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "0"; + command = "optionsDlg.applyGraphics();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "apply"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/optionsMenu.gui b/Templates/BaseGame/game/data/ui/scripts/guis/optionsMenu.gui new file mode 100644 index 000000000..860850cb3 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/optionsMenu.gui @@ -0,0 +1,5194 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(OptionsMenu) { + position = "0 0"; + extent = "1280 720"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + returnGui = "MainMenuGui"; + tamlReader = "17544"; + tile = "0"; + useVariable = "0"; + + new GuiChunkedBitmapCtrl(OptionsMenuBG) { + useVariable = "0"; + tile = "0"; + position = "278 -645"; + extent = "1440 900"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextCtrl(OptionsMenuHeader) { + text = "Controls"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "59 63"; + extent = "129 43"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "51 118"; + extent = "700 518"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl(OptionsMain) { + position = "1 1"; + extent = "700 320"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl(ControlsSettingsMenuButton) { + text = "Controls Settings"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(GraphicsSettingsMenuButton) { + text = "Graphics Settings"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 35"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(AudioSettingsMenuButton) { + text = "Audio Settings"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 105"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ScreenBrSettingsMenuButton) { + text = "Screen Brightness"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 140"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(OptionsOKButton) { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 285"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(OptionsMenu);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuOKButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(OptionsCancelButton) { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "233 285"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuCancelButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(OptionsDefaultButton) { + text = "Defaults"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 285"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuDefaultsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl(GraphicsMenu) { + position = "1 -1"; + extent = "700 519"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Display"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "175 25"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "DisplaySettingsMenu.hidden = false;\nGeneralGraphicsSettingsMenu.hidden = true;"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "General"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "175 0"; + extent = "175 25"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "DisplaySettingsMenu.hidden = true;\nGeneralGraphicsSettingsMenu.hidden = false;"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer(DisplaySettingsMenu) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 25"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "0 0"; + extent = "450 80"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Graphics Driver"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Driver"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuDriver) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "NVIDIA GeForce GTX 950 (D3D9)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "275 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + position = "0 72"; + extent = "450 220"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Display Settings"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Resolution"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuResolution) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "1280 x 720 (16:9)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Full Screen"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuFullScreen) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Refresh Rate"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuRefreshRate) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "60"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 80"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "VSync"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuVSync) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 120"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Field of View"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "65 90"; + ticks = "25"; + snap = "1"; + value = "90"; + position = "190 0"; + extent = "209 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Player::defaultFov"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "90"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "410 0"; + extent = "40 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Player::defaultFov"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 160"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Gamma"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "2 2.5"; + ticks = "0"; + snap = "0"; + value = "2"; + position = "190 0"; + extent = "261 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Video::Gamma"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 180"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Brightness"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "-0.5 0.5"; + ticks = "0"; + snap = "0"; + value = "0"; + position = "190 0"; + extent = "261 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Video::Brightness"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 200"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Contrast"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.5 1.5"; + ticks = "0"; + snap = "0"; + value = "1"; + position = "190 0"; + extent = "261 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Video::Contrast"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiContainer(GeneralGraphicsSettingsMenu) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 25"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(GraphisMenuPageText) { + text = "Overall Quality"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 0"; + extent = "116 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuOverallQlty) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "142 0"; + extent = "548 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 25"; + extent = "350 80"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Lighting"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "350 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Shadow Quality"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuShadowQlty) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Shadow Caching"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuShadowCache) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Shadows::useShadowCaching"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Soft Shadows"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuSoftShadow) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + position = "0 140"; + extent = "350 120"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Models and Textures"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "350 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Model Detail"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuModelDtl) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Texture Detail"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuTextureDtl) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Terrain Detail"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuTerrainDtl) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 80"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Decal Lifetime"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuDecalLife) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 100"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Ground Clutter Density"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuGroundClutter) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + position = "350 25"; + extent = "350 220"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Effects"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "350 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Material Quality"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuMaterialQlty) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "High"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsQualityPopup"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "High Dynamic Range"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuHDR) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableHDR"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Parallax"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuParallax) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 80"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableSSAO"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Ambient Occlusion"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuAO) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableSSAO"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 100"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Light Rays"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuLightRay) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableLightRays"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 120"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Depth of Field"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuDOF) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableDOF"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 160"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Water Reflections"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuWaterRefl) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 140"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Vignetting"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(GraphicsMenuVignette) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "250 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::PostFX::EnableVignette"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 180"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Anti Aliasing"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuAA) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "4x"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 200"; + extent = "350 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Anisotropic Filtering"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GraphicsMenuAniso) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "16X"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "GraphicsMenuOKButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "233 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuCancelButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Defaults"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "GraphicsMenu.Autodetect();\nGraphicsMenu.loadSettings();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl(ScreenBrightnessMenu) { + position = "1 1"; + extent = "700 519"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiChunkedBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/ScreenBrightness_Dark.png"; + wrap = "0"; + position = "0 100"; + extent = "350 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "data/ui/art/ScreenBrightness_Light.png"; + wrap = "0"; + position = "350 100"; + extent = "350 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Adjust the brightness until only the right side image is visible."; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 400"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuOKButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "233 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuCancelButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(BrightnessMenuDefaultsButton) { + text = "Defaults"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuDefaultsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiChunkedBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + useVariable = "0"; + tile = "0"; + position = "0 450"; + extent = "700 34"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiTextCtrl() { + text = "Screen Brightness"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "350 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl(BrightnessMenuSlider) { + range = "0.001 2.2"; + ticks = "9"; + snap = "1"; + value = "1.1005"; + position = "350 9"; + extent = "340 68"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Video::Gamma"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl(ControlsMenu) { + position = "1 1"; + extent = "700 519"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Keyboard"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "175 25"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "KeyboardControlPanel.hidden = false;\nMouseControlPanel.hidden = true;"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Mouse"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "175 0"; + extent = "175 25"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "KeyboardControlPanel.hidden = true;\nMouseControlPanel.hidden = false;"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Gamepad"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "350 0"; + extent = "175 25"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "0"; + active = "1"; + command = "DisplaySettingsMenu.hidden = true;\nGeneralGraphicsSettingsMenu.hidden = false;"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl(KeyboardControlPanel) { + position = "0 25"; + extent = "700 460"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Control Set"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 0"; + extent = "116 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(ControlSetList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Movement"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "142 0"; + extent = "548 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer(ControlsSettingsMenu) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 20"; + extent = "700 440"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 440"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiDynamicCtrlArrayControl(ControlsMenuOptionsArray) { + colCount = "1"; + colSize = "700"; + rowCount = "5"; + rowSize = "35"; + rowSpacing = "0"; + colSpacing = "0"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "1"; + dynamicSize = "0"; + padding = "0 0 0 0"; + position = "1 1"; + extent = "685 440"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiContainer(MouseControlPanel) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 25"; + extent = "700 460"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 460"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + position = "0 0"; + extent = "490 202"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 20"; + extent = "490 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Invert Vertical"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "215 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ControlMenuInvertMouse) { + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "258 0"; + extent = "20 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::invertVerticalMouse"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "490 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Vertical Sensitivity"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "215 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "215 0"; + extent = "220 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::VertMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "440 0"; + extent = "50 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::VertMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "490 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Horizontal Sensitivity"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "215 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "215 0"; + extent = "220 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::HorzMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "440 0"; + extent = "50 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::HorzMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 80"; + extent = "490 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Zoom Vertical Sensitivity"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "215 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.3"; + position = "215 0"; + extent = "220 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::ZoomVertMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "0.3"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "440 0"; + extent = "50 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::ZoomVertMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 100"; + extent = "490 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Zoom Horizontal Sensitivity"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "215 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.3"; + position = "215 0"; + extent = "220 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::ZoomHorzMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "0.3"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "440 0"; + extent = "50 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + variable = "$pref::Input::ZoomHorzMouseSensitivity"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "ControlsMenuOKButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "233 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuCancelButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Defaults"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "ControlsMenuDefaultsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl(AudioMenu) { + position = "1 1"; + extent = "700 519"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 450"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(AudioMenuPageText) { + text = "Audio"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer(AudioSettingsMenu) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 40"; + extent = "700 442"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiDynamicCtrlArrayControl(AudioMenuOptionsArray) { + colCount = "1"; + colSize = "700"; + rowCount = "6"; + rowSize = "35"; + rowSpacing = "0"; + colSpacing = "0"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "1"; + dynamicSize = "0"; + padding = "0 0 0 0"; + position = "0 0"; + extent = "700 444"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsMenuSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "35 0"; + extent = "180 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "XAudio"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "35 0"; + extent = "180 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "SettingText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = ">"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "215 0"; + extent = "35 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuForwardSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "<"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "35 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuBackSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Sound Provider"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 35"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "GraphicsMenuSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "35 0"; + extent = "180 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Speakers (High Definition Audio Device)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "35 0"; + extent = "180 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "SettingText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = ">"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "215 0"; + extent = "35 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuForwardSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "<"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "35 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuBackSetting"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Sound Device"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 70"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.8"; + position = "0 0"; + extent = "200 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::masterVolume"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "slider"; + class = "OptionsMenuSlider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "8"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "200 0"; + extent = "50 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "valueText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Master Audio Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 105"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.1"; + position = "0 0"; + extent = "200 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[1]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "slider"; + class = "OptionsMenuSlider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "10"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "200 0"; + extent = "50 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "valueText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Gui Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 140"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.1"; + position = "0 0"; + extent = "200 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[2]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "slider"; + class = "OptionsMenuSlider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "10"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "200 0"; + extent = "50 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "valueText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Effect Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 175"; + extent = "700 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "450 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + wrap = "0"; + position = "0 0"; + extent = "250 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "0.1"; + position = "0 0"; + extent = "200 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[4]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "slider"; + class = "OptionsMenuSlider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "10"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "200 0"; + extent = "50 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "valueText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Music Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "450 35"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "nameText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiControl() { + position = "0 40"; + extent = "450 140"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Sound Driver"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(AudioMenuSoundDriver) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "XAudio"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "265 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 20"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Sound Device"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(AudioMenuSoundDevice) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "Speakers (High Definition Audio Device)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "175 0"; + extent = "265 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 60"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Master Audio Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "190 0"; + extent = "209 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::masterVolume"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "410 0"; + extent = "40 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::masterVolume"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 80"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Gui Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "190 0"; + extent = "209 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[1]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "410 0"; + extent = "40 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[1]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 100"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Effect Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "190 0"; + extent = "209 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[2]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "410 0"; + extent = "40 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[2]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "0 120"; + extent = "450 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Music Volume"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "175 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0.1 1"; + ticks = "8"; + snap = "1"; + value = "1"; + position = "190 0"; + extent = "209 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[4]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "410 0"; + extent = "40 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + variable = "$pref::SFX::channelVolume[4]"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "AudioMenuOKButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "233 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuCancelButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(AudioMenuDefaultsButton) { + text = "Defaults"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 484"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuDefaultsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/pauseMenu.gui b/Templates/BaseGame/game/data/ui/scripts/guis/pauseMenu.gui new file mode 100644 index 000000000..5995cdf36 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/pauseMenu.gui @@ -0,0 +1,153 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(PauseMenu) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + tamlReader = "19772"; + tile = "0"; + useVariable = "0"; + + new GuiChunkedBitmapCtrl(PauseMenuBG) { + bitmap = "data/ui/art/hudfill"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiControl() { + position = "51 118"; + extent = "700 518"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl(PauseOptionsMain) { + position = "1 1"; + extent = "700 320"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl(PauseMenuExitButton) { + text = "Exit Game"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 0"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "escapeFromGame();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(PauseMenuOptionsButton) { + text = "Options"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 35"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "PauseMenu.openOptionsMenu();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(PauseMenuControlsButton) { + text = "Controls"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "0 70"; + extent = "700 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "PauseMenu.openControlsMenu();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(PauseMenuCancelButton) { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + position = "466 285"; + extent = "233 35"; + minExtent = "8 8"; + horizSizing = "relative"; + vertSizing = "bottom"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "OptionsMenuDefaultsButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + accelerator="escape"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/profiler.gui b/Templates/BaseGame/game/data/ui/scripts/guis/profiler.gui new file mode 100644 index 000000000..7c69006c2 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/profiler.gui @@ -0,0 +1,661 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ProfilerGui) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl(ppProfilerWindow) { + text = "Profiler Manager"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "Canvas.popDialog(ProfilerGui);"; + edgeSnap = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "306 216"; + extent = "415 199"; + minExtent = "8 8"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiCheckBoxCtrl(ppShowFps) { + text = "Show Fps"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 24"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Fps counter"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowGfx) { + text = "Show Gfx"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 40"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Gfx"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowShadow) { + text = "Show Shadow"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 56"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Shdow"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowNet) { + text = "Show Net"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 136"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Network activity"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowForest) { + text = "Show Forest"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 120"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Forest"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowGroundcover) { + text = "Show Groundcover"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 104"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Groundcover"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowTerrain) { + text = "Show Terrain"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 88"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Terrain"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(ppShowSfx) { + text = "Show Sfx"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 72"; + extent = "127 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable or Disable the Sfx"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsEnableDisable) { + text = "On/Off OSD"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "309 38"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "showMetrics(true);"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Apply the setting from the dialog box"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsUpdate) { + text = "Update"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "200 38"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "showMetrics(false);"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Update the profiler with the new parameters"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(ppOptionsDoProfiling) { + text = "DoProfiling"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "309 141"; + extent = "93 23"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "doProfileFromGui();"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Does the profile for the ammount of time specified "; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(DurationLabel) { + text = "Duration[ms]"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "200 144"; + extent = "39 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Duration) { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "1000"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "249 144"; + extent = "53 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +$MetricsParamArray[0] = "fps "; +$MetricsParamArray[1] = "shadow "; +$MetricsParamArray[2] = "gfx "; +$MetricsParamArray[3] = "sfx "; +$MetricsParamArray[4] = "terrain "; +$MetricsParamArray[5] = "groundcover "; +$MetricsParamArray[6] = "forest "; +$MetricsParamArray[7] = "net "; +$EnableProfiler = false; +$string = ""; //string used to collet the parameters for metrics function + +function showMetrics(%var) +{ + $string = ""; + if(ppShowFps.getValue()) + { + $string = $string @ $MetricsParamArray[0]; + } + if(ppShowShadow.getValue()) + { + $string = $string @ $MetricsParamArray[1]; + } + if(ppShowGfx.getValue()) + { + $string = $string @ $MetricsParamArray[2]; + } + if(ppShowSfx.getValue()) + { + $string = $string @ $MetricsParamArray[3]; + } + if(ppShowTerrain.getValue()) + { + $string = $string @ $MetricsParamArray[4]; + } + if(ppShowForest.getValue()) + { + $string = $string @ $MetricsParamArray[5]; + } + if(ppShowGroundcover.getValue()) + { + $string = $string @ $MetricsParamArray[6]; + } + if(ppShowNet.getValue()) + { + $string = $string @ $MetricsParamArray[7]; + } + + if(%var) + { + $EnableProfiler = !($EnableProfiler); + + if($EnableProfiler) + { + metrics($string); + } + else if((false == $EnableProfiler)) + { + metrics(); + } + } + else if($EnableProfiler) //will enter only when the enable/disable button was pressed + { + metrics($string); + } +} + +function showMetics(%var) +{ + if(%var) + { + metrics($string); + } + else if(true == $EnableProfiler) + { + $EnableProfiler = false; + metrics(); + } +} + +GlobalActionMap.bind(keyboard, "ctrl F2", showMetics); + +%guiContent = new GuiControl(FrameOverlayGui) { + profile = "GuiModelessDialogProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "True"; + modal = "false"; + helpTag = "0"; + noCursor = true; + new GuiConsoleTextCtrl(TextOverlayControl) { + profile = "GuiConsoleTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 5"; + extent = "130 18"; + minExtent = "4 4"; + visible = "True"; + setFirstResponder = "True"; + modal = "True"; + helpTag = "0"; + expression = "10"; + command = "Canvas.popDialog(FrameOverlayGui);"; + accelerator = "escape"; + }; +}; + +// Note: To implement your own metrics overlay +// just add a function with a name in the form +// XXXXMetricsCallback which can be enabled via +// metrics( XXXX ) + +function fpsMetricsCallback() +{ + return " | FPS |" @ + " " @ $fps::real @ + " max: " @ $fps::realMax @ + " min: " @ $fps::realMin @ + " mspf: " @ 1000 / $fps::real; +} + +function gfxMetricsCallback() +{ + return " | GFX |" @ + " PolyCount: " @ $GFXDeviceStatistics::polyCount @ + " DrawCalls: " @ $GFXDeviceStatistics::drawCalls @ + " RTChanges: " @ $GFXDeviceStatistics::renderTargetChanges; + +} + +function terrainMetricsCallback() +{ + return " | Terrain |" @ + " Cells: " @ $TerrainBlock::cellsRendered @ + " Override Cells: " @ $TerrainBlock::overrideCells @ + " DrawCalls: " @ $TerrainBlock::drawCalls; +} + +function netMetricsCallback() +{ + return " | Net |" @ + " BitsSent: " @ $Stats::netBitsSent @ + " BitsRcvd: " @ $Stats::netBitsReceived @ + " GhostUpd: " @ $Stats::netGhostUpdates; +} + +function groundCoverMetricsCallback() +{ + return " | GroundCover |" @ + " Cells: " @ $GroundCover::renderedCells @ + " Billboards: " @ $GroundCover::renderedBillboards @ + " Batches: " @ $GroundCover::renderedBatches @ + " Shapes: " @ $GroundCover::renderedShapes; +} + +function forestMetricsCallback() +{ + return " | Forest |" @ + " Cells: " @ $Forest::totalCells @ + " Cells Meshed: " @ $Forest::cellsRendered @ + " Cells Billboarded: " @ $Forest::cellsBatched @ + " Meshes: " @ $Forest::cellItemsRendered @ + " Billboards: " @ $Forest::cellItemsBatched; +} + +function sfxMetricsCallback() +{ + return " | SFX |" @ + " Sounds: " @ $SFX::numSounds @ + " Lists: " @ ( $SFX::numSources - $SFX::numSounds - $SFX::Device::fmodNumEventSource ) @ + " Events: " @ $SFX::fmodNumEventSources @ + " Playing: " @ $SFX::numPlaying @ + " Culled: " @ $SFX::numCulled @ + " Voices: " @ $SFX::numVoices @ + " Buffers: " @ $SFX::Device::numBuffers @ + " Memory: " @ ( $SFX::Device::numBufferBytes / 1024.0 / 1024.0 ) @ " MB" @ + " Time/S: " @ $SFX::sourceUpdateTime @ + " Time/P: " @ $SFX::parameterUpdateTime @ + " Time/A: " @ $SFX::ambientUpdateTime; +} + +function sfxSourcesMetricsCallback() +{ + return sfxDumpSourcesToString(); +} + +function sfxStatesMetricsCallback() +{ + return " | SFXStates |" @ sfxGetActiveStates(); +} + +function timeMetricsCallback() +{ + return " | Time |" @ + " Sim Time: " @ getSimTime() @ + " Mod: " @ getSimTime() % 32; +} + +function reflectMetricsCallback() +{ + return " | REFLECT |" @ + " Objects: " @ $Reflect::numObjects @ + " Visible: " @ $Reflect::numVisible @ + " Occluded: " @ $Reflect::numOccluded @ + " Updated: " @ $Reflect::numUpdated @ + " Elapsed: " @ $Reflect::elapsed NL + + " Allocated: " @ $Reflect::renderTargetsAllocated @ + " Pooled: " @ $Reflect::poolSize NL + + " " @ getWord( $Reflect::textureStats, 1 ) TAB + " " @ getWord( $Reflect::textureStats, 2 ) @ "MB" TAB + " " @ getWord( $Reflect::textureStats, 0 ); +} + +function decalMetricsCallback() +{ + return " | DECAL |" @ + " Batches: " @ $Decal::Batches @ + " Buffers: " @ $Decal::Buffers @ + " DecalsRendered: " @ $Decal::DecalsRendered; +} + +function renderMetricsCallback() +{ + return " | Render |" @ + " Mesh: " @ $RenderMetrics::RIT_Mesh @ + " MeshDL: " @ $RenderMetrics::RIT_MeshDynamicLighting @ + " Shadow: " @ $RenderMetrics::RIT_Shadow @ + " Sky: " @ $RenderMetrics::RIT_Sky @ + " Obj: " @ $RenderMetrics::RIT_Object @ + " ObjT: " @ $RenderMetrics::RIT_ObjectTranslucent @ + " Decal: " @ $RenderMetrics::RIT_Decal @ + " Water: " @ $RenderMetrics::RIT_Water @ + " Foliage: " @ $RenderMetrics::RIT_Foliage @ + " Trans: " @ $RenderMetris::RIT_Translucent @ + " Custom: " @ $RenderMetrics::RIT_Custom; +} + +function shadowMetricsCallback() +{ + return " | Shadow |" @ + " Active: " @ $ShadowStats::activeMaps @ + " Updated: " @ $ShadowStats::updatedMaps @ + " PolyCount: " @ $ShadowStats::polyCount @ + " DrawCalls: " @ $ShadowStats::drawCalls @ + " RTChanges: " @ $ShadowStats::rtChanges @ + " PoolTexCount: " @ $ShadowStats::poolTexCount @ + " PoolTexMB: " @ $ShadowStats::poolTexMemory @ "MB"; +} + +function basicShadowMetricsCallback() +{ + return " | Shadow |" @ + " Active: " @ $BasicLightManagerStats::activePlugins @ + " Updated: " @ $BasicLightManagerStats::shadowsUpdated @ + " Elapsed Ms: " @ $BasicLightManagerStats::elapsedUpdateMs; +} + +function lightMetricsCallback() +{ + return " | Deferred Lights |" @ + " Active: " @ $lightMetrics::activeLights @ + " Culled: " @ $lightMetrics::culledLights; +} + +function particleMetricsCallback() +{ + return " | Particles |" @ + " # Simulated " @ $particle::numSimulated; +} +function partMetricsCallback() +{ + return particleMetricsCallback(); +} + + +// alias +function audioMetricsCallback() +{ + return sfxMetricsCallback(); +} + +// alias +function videoMetricsCallback() +{ + return gfxMetricsCallback(); +} + +// Add a metrics HUD. %expr can be a vector of names where each element +// must have a corresponding 'MetricsCallback()' function defined +// that will be called on each update of the GUI control. The results +// of each function are stringed together. +// +// Example: metrics( "fps gfx" ); + +function metrics( %expr ) +{ + %metricsExpr = ""; + if( %expr !$= "" ) + { + for( %i = 0;; %i ++ ) + { + %name = getWord( %expr, %i ); + if( %name $= "" ) + break; + else + { + %cb = %name @ "MetricsCallback"; + if( !isFunction( %cb ) ) + error( "metrics - undefined callback: " @ %cb ); + else + { + %cb = %cb @ "()"; + if( %i > 0 ) + %metricsExpr = %metricsExpr @ " NL "; + %metricsExpr = %metricsExpr @ %cb; + } + } + } + + if( %metricsExpr !$= "" ) + %metricsExpr = %metricsExpr @ " @ \" \""; + } + + if( %metricsExpr !$= "" ) + { + $GameCanvas.pushDialog( FrameOverlayGui, 1000 ); + TextOverlayControl.setValue( %metricsExpr ); + } + else + $GameCanvas.popDialog(FrameOverlayGui); +} diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/remapConfirmDlg.gui b/Templates/BaseGame/game/data/ui/scripts/guis/remapConfirmDlg.gui new file mode 100644 index 000000000..e0489a9f8 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/remapConfirmDlg.gui @@ -0,0 +1,125 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(RemapConfirmDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + helpTag = "0"; + + new GuiContainer(RemapConfirmationPanel) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "168 352"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiChunkedBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(RemapConfirmationText) { + text = "\"m\" is already bound to \"Forward\"!\nDo you wish to replace this mapping?"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 8"; + extent = "700 20"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + accelerator = "return"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(RemapConfirmationYesButton) { + text = "Yes"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "270 36"; + extent = "80 22"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "ControlsMenu.redoMapping(keyboard, \"m\", \"jump\", 0, 4); Canvas.popDialog();"; + accelerator = "return"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(RemapConfirmationNoButton) { + text = "No"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "367 36"; + extent = "80 22"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog();"; + accelerator = "escape"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/guis/remapDlg.gui b/Templates/BaseGame/game/data/ui/scripts/guis/remapDlg.gui new file mode 100644 index 000000000..a4dd8edac --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/guis/remapDlg.gui @@ -0,0 +1,122 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(RemapDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + helpTag = "0"; + + new GuiContainer(RemapPanel) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "162 352"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "center"; + vertSizing = "center"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiInputCtrl(OptRemapInputCtrl) { + lockMouse = "0"; + position = "480 0"; + extent = "64 64"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiInputCtrlProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiChunkedBitmapCtrl() { + bitmap = "data/ui/art/hudfill.png"; + useVariable = "0"; + tile = "0"; + position = "0 0"; + extent = "700 64"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Press escape to cancel"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "247 34"; + extent = "242 20"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Re-bind \"\" to..."; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "177 8"; + extent = "384 20"; + minExtent = "8 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiMenuButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OptRemapText"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/data/ui/scripts/joinServerMenu.cs b/Templates/BaseGame/game/data/ui/scripts/joinServerMenu.cs new file mode 100644 index 000000000..5529ebaed --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/joinServerMenu.cs @@ -0,0 +1,143 @@ + +function JoinServerMenu::onWake() +{ + // Double check the status. Tried setting this the control + // inactive to start with, but that didn't seem to work. + JoinServerJoinBtn.setActive(JS_serverList.rowCount() > 0); +} + +//---------------------------------------- +function JoinServerMenu::query(%this) +{ + queryMasterServer( + 0, // Query flags + $Client::GameTypeQuery, // gameTypes + $Client::MissionTypeQuery, // missionType + 0, // minPlayers + 100, // maxPlayers + 0, // maxBots + 2, // regionMask + 0, // maxPing + 100, // minCPU + 0 // filterFlags + ); +} + +//---------------------------------------- +function JoinServerMenu::queryLan(%this) +{ + queryLANServers( + $pref::Net::Port, // lanPort for local queries + 0, // Query flags + $Client::GameTypeQuery, // gameTypes + $Client::MissionTypeQuery, // missionType + 0, // minPlayers + 100, // maxPlayers + 0, // maxBots + 2, // regionMask + 0, // maxPing + 100, // minCPU + 0 // filterFlags + ); +} + +//---------------------------------------- +function JoinServerMenu::cancel(%this) +{ + cancelServerQuery(); + JS_queryStatus.setVisible(false); +} + + +//---------------------------------------- +function JoinServerMenu::join(%this) +{ + cancelServerQuery(); + %index = JS_serverList.getSelectedId(); + + JoinGame(%index); +} + +//---------------------------------------- +function JoinServerMenu::refresh(%this) +{ + cancelServerQuery(); + %index= JS_serverList.getSelectedId(); + + // The server info index is stored in the row along with the + // rest of displayed info. + if( setServerInfo( %index ) ) + querySingleServer( $ServerInfo::Address, 0 ); +} + +//---------------------------------------- +function JoinServerMenu::refreshSelectedServer( %this ) +{ + querySingleServer( $JoinGameAddress, 0 ); +} + +//---------------------------------------- +function JoinServerMenu::exit(%this) +{ + cancelServerQuery(); + + Canvas.popDialog(JoinServerMenu); +} + +//---------------------------------------- +function JoinServerMenu::update(%this) +{ + // Copy the servers into the server list. + JS_queryStatus.setVisible(false); + JS_serverList.clear(); + %sc = getServerCount(); + for( %i = 0; %i < %sc; %i ++ ) { + setServerInfo(%i); + JS_serverList.addRow( %i, + $ServerInfo::Name TAB + $ServerInfo::Ping TAB + $ServerInfo::PlayerCount @ "/" @ $ServerInfo::MaxPlayers TAB + $ServerInfo::Version TAB + $ServerInfo::MissionName + ); + } + JS_serverList.sort(0); + JS_serverList.setSelectedRow(0); + JS_serverList.scrollVisible(0); + + JoinServerJoinBtn.setActive(JS_serverList.rowCount() > 0); +} + +//---------------------------------------- +function onServerQueryStatus(%status, %msg, %value) +{ + echo("ServerQuery: " SPC %status SPC %msg SPC %value); + // Update query status + // States: start, update, ping, query, done + // value = % (0-1) done for ping and query states + if (!JS_queryStatus.isVisible()) + JS_queryStatus.setVisible(true); + + switch$ (%status) { + case "start": + JoinServerJoinBtn.setActive(false); + JoinServerQryInternetBtn.setActive(false); + JS_statusText.setText(%msg); + JS_statusBar.setValue(0); + JS_serverList.clear(); + + case "ping": + JS_statusText.setText("Ping Servers"); + JS_statusBar.setValue(%value); + + case "query": + JS_statusText.setText("Query Servers"); + JS_statusBar.setValue(%value); + + case "done": + JoinServerQryInternetBtn.setActive(true); + JS_queryStatus.setVisible(false); + JS_status.setText(%msg); + JoinServerMenu.update(); + } +} diff --git a/Templates/BaseGame/game/data/ui/scripts/mainMenu.cs b/Templates/BaseGame/game/data/ui/scripts/mainMenu.cs new file mode 100644 index 000000000..3a3b22f61 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/mainMenu.cs @@ -0,0 +1,41 @@ +function MainMenuGui::onWake(%this) +{ + if (isFunction("getWebDeployment") && + getWebDeployment() && + isObject(%this-->ExitButton)) + %this-->ExitButton.setVisible(false); + + MainMenuButtonContainer.hidden = false; +} + +function MainMenuGui::openSinglePlayerMenu(%this) +{ + $pref::HostMultiPlayer=false; + Canvas.pushDialog(ChooseLevelDlg); + ChooseLevelDlg.returnGui = %this; + MainMenuButtonContainer.hidden = true; + MainMenuAppLogo.setBitmap("data/ui/art/Torque-3D-logo"); +} + +function MainMenuGui::openMultiPlayerMenu(%this) +{ + $pref::HostMultiPlayer=true; + Canvas.pushDialog(ChooseLevelDlg); + ChooseLevelDlg.returnGui = %this; + MainMenuButtonContainer.hidden = true; + MainMenuAppLogo.setBitmap("data/ui/art/Torque-3D-logo"); +} + +function MainMenuGui::openOptionsMenu(%this) +{ + Canvas.pushDialog(OptionsMenu); + OptionsMenu.returnGui = %this; + MainMenuButtonContainer.hidden = true; + MainMenuAppLogo.setBitmap("data/ui/art/Torque-3D-logo"); +} + +function MainMenuGui::onReturnTo(%this) +{ + MainMenuButtonContainer.hidden = false; + MainMenuAppLogo.setBitmap("data/ui/art/Torque-3D-logo-shortcut"); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/messageBoxes.cs b/Templates/BaseGame/game/data/ui/scripts/messageBoxes.cs new file mode 100644 index 000000000..98a121db6 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/messageBoxes.cs @@ -0,0 +1,301 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// Cleanup Dialog created by 'core' +if( isObject( MessagePopupDlg ) ) + MessagePopupDlg.delete(); +if( isObject( MessageBoxYesNoDlg ) ) + MessageBoxYesNoDlg.delete(); +if( isObject( MessageBoxYesNoCancelDlg ) ) + MessageBoxYesNoCancelDlg.delete(); +if( isObject( MessageBoxOKCancelDetailsDlg ) ) + MessageBoxOKCancelDetailsDlg.delete(); +if( isObject( MessageBoxOKCancelDlg ) ) + MessageBoxOKCancelDlg.delete(); +if( isObject( MessageBoxOKDlg ) ) + MessageBoxOKDlg.delete(); +if( isObject( IODropdownDlg ) ) + IODropdownDlg.delete(); + + +// Load Editor Dialogs +exec("./guis/messageBoxOk.ed.gui"); +exec("./guis/messageBoxYesNo.ed.gui"); + +// -------------------------------------------------------------------- +// Message Sound +// -------------------------------------------------------------------- +/*new SFXDescription(MessageBoxAudioDescription) +{ + volume = 1.0; + isLooping = false; + is3D = false; + channel = $GuiAudioType; +}; + +new SFXProfile(messageBoxBeep) +{ + filename = "./messageBoxSound"; + description = MessageBoxAudioDescription; + preload = true; +};*/ + + + + +//--------------------------------------------------------------------------------------------- +// messageCallback +// Calls a callback passed to a message box. +//--------------------------------------------------------------------------------------------- +function messageCallback(%dlg, %callback) +{ + Canvas.popDialog(%dlg); + eval(%callback); +} + +//The # in the function passed replaced with the output +//of the preset menu. +function IOCallback(%dlg, %callback) +{ + %id = IODropdownMenu.getSelected(); + %text = IODropdownMenu.getTextById(%id); + %callback = strreplace(%callback, "#", %text); + eval(%callback); + + Canvas.popDialog(%dlg); +} + +//--------------------------------------------------------------------------------------------- +// MBSetText +// Sets the text of a message box and resizes it to accomodate the new string. +//--------------------------------------------------------------------------------------------- +function MBSetText(%text, %frame, %msg) +{ + // Get the extent of the text box. + %ext = %text.getExtent(); + // Set the text in the center of the text box. + %text.setText("" @ %msg); + // Force the textbox to resize itself vertically. + %text.forceReflow(); + // Grab the new extent of the text box. + %newExtent = %text.getExtent(); + + // Get the vertical change in extent. + %deltaY = getWord(%newExtent, 1) - getWord(%ext, 1); + + // Resize the window housing the text box. + %windowPos = %frame.getPosition(); + %windowExt = %frame.getExtent(); + %frame.resize(getWord(%windowPos, 0), getWord(%windowPos, 1) - (%deltaY / 2), + getWord(%windowExt, 0), getWord(%windowExt, 1) + %deltaY); + + %frame.canMove = "0"; + //%frame.canClose = "0"; + %frame.resizeWidth = "0"; + %frame.resizeHeight = "0"; + %frame.canMinimize = "0"; + %frame.canMaximize = "0"; + + //sfxPlayOnce( messageBoxBeep ); +} + +//--------------------------------------------------------------------------------------------- +// Various message box display functions. Each one takes a window title, a message, and a +// callback for each button. +//--------------------------------------------------------------------------------------------- + +function MessageBoxOK(%title, %message, %callback) +{ + MBOKFrame.text = %title; + Canvas.pushDialog(MessageBoxOKDlg); + MBSetText(MBOKText, MBOKFrame, %message); + MessageBoxOKDlg.callback = %callback; +} + +function MessageBoxOKDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxOKCancel(%title, %message, %callback, %cancelCallback) +{ + MBOKCancelFrame.text = %title; + Canvas.pushDialog(MessageBoxOKCancelDlg); + MBSetText(MBOKCancelText, MBOKCancelFrame, %message); + MessageBoxOKCancelDlg.callback = %callback; + MessageBoxOKCancelDlg.cancelCallback = %cancelCallback; +} + +function MessageBoxOKCancelDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxOKCancelDetails(%title, %message, %details, %callback, %cancelCallback) +{ + if(%details $= "") + { + MBOKCancelDetailsButton.setVisible(false); + } + + MBOKCancelDetailsScroll.setVisible(false); + + MBOKCancelDetailsFrame.setText( %title ); + + Canvas.pushDialog(MessageBoxOKCancelDetailsDlg); + MBSetText(MBOKCancelDetailsText, MBOKCancelDetailsFrame, %message); + MBOKCancelDetailsInfoText.setText(%details); + + %textExtent = MBOKCancelDetailsText.getExtent(); + %textExtentY = getWord(%textExtent, 1); + %textPos = MBOKCancelDetailsText.getPosition(); + %textPosY = getWord(%textPos, 1); + + %extentY = %textPosY + %textExtentY + 65; + + MBOKCancelDetailsInfoText.setExtent(285, 128); + + MBOKCancelDetailsFrame.setExtent(300, %extentY); + + MessageBoxOKCancelDetailsDlg.callback = %callback; + MessageBoxOKCancelDetailsDlg.cancelCallback = %cancelCallback; + + MBOKCancelDetailsFrame.defaultExtent = MBOKCancelDetailsFrame.getExtent(); +} + +function MBOKCancelDetailsToggleInfoFrame() +{ + if(!MBOKCancelDetailsScroll.isVisible()) + { + MBOKCancelDetailsScroll.setVisible(true); + MBOKCancelDetailsText.forceReflow(); + %textExtent = MBOKCancelDetailsText.getExtent(); + %textExtentY = getWord(%textExtent, 1); + %textPos = MBOKCancelDetailsText.getPosition(); + %textPosY = getWord(%textPos, 1); + + %verticalStretch = %textExtentY; + + if((%verticalStretch > 260) || (%verticalStretch < 0)) + %verticalStretch = 260; + + %extent = MBOKCancelDetailsFrame.defaultExtent; + %height = getWord(%extent, 1); + + %posY = %textPosY + %textExtentY + 10; + %posX = getWord(MBOKCancelDetailsScroll.getPosition(), 0); + MBOKCancelDetailsScroll.setPosition(%posX, %posY); + MBOKCancelDetailsScroll.setExtent(getWord(MBOKCancelDetailsScroll.getExtent(), 0), %verticalStretch); + MBOKCancelDetailsFrame.setExtent(300, %height + %verticalStretch + 10); + } else + { + %extent = MBOKCancelDetailsFrame.defaultExtent; + %width = getWord(%extent, 0); + %height = getWord(%extent, 1); + MBOKCancelDetailsFrame.setExtent(%width, %height); + MBOKCancelDetailsScroll.setVisible(false); + } +} + +function MessageBoxOKCancelDetailsDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxYesNo(%title, %message, %yesCallback, %noCallback) +{ + MBYesNoFrame.text = %title; + Canvas.pushDialog(MessageBoxYesNoDlg); + MBSetText(MBYesNoText, MBYesNoFrame, %message); + MessageBoxYesNoDlg.yesCallBack = %yesCallback; + MessageBoxYesNoDlg.noCallback = %noCallBack; +} + +function MessageBoxYesNoCancel(%title, %message, %yesCallback, %noCallback, %cancelCallback) +{ + MBYesNoCancelFrame.text = %title; + MessageBoxYesNoDlg.profile = "GuiOverlayProfile"; + Canvas.pushDialog(MessageBoxYesNoCancelDlg); + MBSetText(MBYesNoCancelText, MBYesNoCancelFrame, %message); + MessageBoxYesNoCancelDlg.yesCallBack = %yesCallback; + MessageBoxYesNoCancelDlg.noCallback = %noCallBack; + MessageBoxYesNoCancelDlg.cancelCallback = %cancelCallback; +} + +function MessageBoxYesNoDlg::onSleep( %this ) +{ + %this.yesCallback = ""; + %this.noCallback = ""; +} + +//--------------------------------------------------------------------------------------------- +// MessagePopup +// Displays a message box with no buttons. Disappears after %delay milliseconds. +//--------------------------------------------------------------------------------------------- +function MessagePopup(%title, %message, %delay) +{ + // Currently two lines max. + MessagePopFrame.setText(%title); + Canvas.pushDialog(MessagePopupDlg); + MBSetText(MessagePopText, MessagePopFrame, %message); + if (%delay !$= "") + schedule(%delay, 0, CloseMessagePopup); +} + +//--------------------------------------------------------------------------------------------- +// IODropdown +// By passing in a simgroup or simset, the user will be able to choose a child of that group +// through a guiPopupMenuCtrl +//--------------------------------------------------------------------------------------------- + +function IODropdown(%title, %message, %simgroup, %callback, %cancelCallback) +{ + IODropdownFrame.text = %title; + Canvas.pushDialog(IODropdownDlg); + MBSetText(IODropdownText, IODropdownFrame, %message); + + if(isObject(%simgroup)) + { + for(%i = 0; %i < %simgroup.getCount(); %i++) + IODropdownMenu.add(%simgroup.getObject(%i).getName()); + + } + + IODropdownMenu.sort(); + IODropdownMenu.setFirstSelected(0); + + IODropdownDlg.callback = %callback; + IODropdownDlg.cancelCallback = %cancelCallback; +} + +function IODropdownDlg::onSleep( %this ) +{ + %this.callback = ""; + %this.cancelCallback = ""; + IODropdownMenu.clear(); +} + +function CloseMessagePopup() +{ + Canvas.popDialog(MessagePopupDlg); +} diff --git a/Templates/BaseGame/game/data/ui/scripts/optionsList.cs b/Templates/BaseGame/game/data/ui/scripts/optionsList.cs new file mode 100644 index 000000000..ad1da0759 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/optionsList.cs @@ -0,0 +1,437 @@ +new SimGroup( MeshQualityGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::TS::detailAdjust"] = 1.5; + key["$pref::TS::skipRenderDLs"] = 0; + }; + new ArrayObject( ) + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::TS::detailAdjust"] = 1.0; + key["$pref::TS::skipRenderDLs"] = 0; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::TS::detailAdjust"] = 0.75; + key["$pref::TS::skipRenderDLs"] = 0; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::TS::detailAdjust"] = 0.5; + key["$pref::TS::skipRenderDLs"] = 1; + }; +}; + +new SimGroup( TextureQualityGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Video::textureReductionLevel"] = 0; + key["$pref::Reflect::refractTexScale"] = 1.25; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::Video::textureReductionLevel"] = 0; + key["$pref::Reflect::refractTexScale"] = 1; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Video::textureReductionLevel"] = 1; + key["$pref::Reflect::refractTexScale"] = 0.75; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::Video::textureReductionLevel"] = 2; + key["$pref::Reflect::refractTexScale"] = 0.5; + }; +}; + +new SimGroup( GroundCoverDensityGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::GroundCover::densityScale"] = 1.0; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::GroundCover::densityScale"] = 0.75; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::GroundCover::densityScale"] = 0.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::GroundCover::densityScale"] = 0.25; + }; +}; + +new SimGroup( DecalLifetimeGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::decalMgr::enabled"] = true; + key["$pref::Decals::lifeTimeScale"] = 1; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::decalMgr::enabled"] = true; + key["$pref::Decals::lifeTimeScale"] = 0.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::decalMgr::enabled"] = true; + key["$pref::Decals::lifeTimeScale"] = 0.25; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "None"; + + key["$pref::decalMgr::enabled"] = false; + }; +}; + +new SimGroup( TerrainQualityGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Terrain::lodScale"] = 0.75; + key["$pref::Terrain::detailScale"] = 1.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::Terrain::lodScale"] = 1.0; + key["$pref::Terrain::detailScale"] = 1.0; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Terrain::lodScale"] = 1.5; + key["$pref::Terrain::detailScale"] = 0.75; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::Terrain::lodScale"] = 2.0; + key["$pref::Terrain::detailScale"] = 0.5; + }; +}; + +//Shadows and Lighting +new SimGroup( ShadowQualityList ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::lightManager"] = "Advanced Lighting"; + key["$pref::Shadows::disable"] = false; + key["$pref::Shadows::textureScalar"] = 1.0; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::lightManager"] = "Advanced Lighting"; + key["$pref::Shadows::disable"] = false; + key["$pref::Shadows::textureScalar"] = 0.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::lightManager"] = "Advanced Lighting"; + key["$pref::Shadows::disable"] = false; + key["$pref::Shadows::textureScalar"] = 0.25; + + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "None"; + + key["$pref::lightManager"] = "Advanced Lighting"; + key["$pref::Shadows::disable"] = true; + key["$pref::Shadows::textureScalar"] = 0.5; + }; +}; + +new SimGroup( ShadowDistanceList ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Highest"; + + key["$pref::Shadows::drawDistance"] = 2; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Shadows::drawDistance"] = 1.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::Shadows::drawDistance"] = 1; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Shadows::drawDistance"] = 0.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::Shadows::drawDistance"] = 0.25; + }; +}; + +new SimGroup( SoftShadowList ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Shadows::filterMode"] = "SoftShadowHighQuality"; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Shadows::filterMode"] = "SoftShadow"; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Off"; + + key["$pref::Shadows::filterMode"] = "None"; + }; +}; + +new SimGroup( LightDistanceList ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Highest"; + + key["$pref::Lights::drawDistance"] = 2; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Lights::drawDistance"] = 1.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Medium"; + + key["$pref::Lights::drawDistance"] = 1; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Lights::drawDistance"] = 0.5; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Lowest"; + + key["$pref::Lights::drawDistance"] = 0.25; + }; +}; + +new SimGroup( ShaderQualityGroup ) +{ + class = "GraphicsOptionsMenuGroup"; + + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "High"; + + key["$pref::Video::disablePixSpecular"] = false; + key["$pref::Video::disableNormalmapping"] = false; + }; + new ArrayObject() + { + class = "GraphicsQualityLevel"; + caseSensitive = true; + + displayName = "Low"; + + key["$pref::Video::disablePixSpecular"] = true; + key["$pref::Video::disableNormalmapping"] = true; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/optionsMenu.cs b/Templates/BaseGame/game/data/ui/scripts/optionsMenu.cs new file mode 100644 index 000000000..40807691c --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/optionsMenu.cs @@ -0,0 +1,473 @@ +//options settings + +//Screen and Display menu +//Renderer Mode +//Screen resolution +//Windowed/fullscreen(borderless?) +//VSync + +//Screen brightness +//screen brightness +//screen gamma + +//Lighting Menu +//Shadow Distance(Distance shadows are drawn to. Also affects shadowmap slices) +//Shadow Quality(Resolution of shadows rendered, setting to none disables dynamic shadows) +//Soft Shadows(Whether shadow softening is used) +//Shadow caching(If the lights enable it, shadow caching is activated) +//Light Draw Distance(How far away lights are still drawn. Doesn't impact vector lights like the sun) + +//Mesh and Textures Menu +//Draw distance(Overall draw distance) -slider +//Object draw distance(Draw distance from small/unimportant objects) -slider +//Mesh quality +//Texture quality +//Foliage draw distance +//Terrain Quality +//Decal Quality + +//Effects Menu +//Parallax +//HDR +//Light shafts +//Motion Blur +//Depth of Field +//SSAO +//AA(ModelXAmount)[defualt is FXAA] +//Anisotropic filtering + +//Keybinds + +//Camera +//horizontal mouse sensitivity +//vert mouse sensitivity +//invert vertical +//zoom mouse sensitivities(both horz/vert) +//headbob +//FOV + +function OptionsMenu::onWake(%this) +{ + OptionsMain.hidden = false; + ControlsMenu.hidden = true; + GraphicsMenu.hidden = true; + AudioMenu.hidden = true; + CameraMenu.hidden = true; + ScreenBrightnessMenu.hidden = true; + + OptionsOKButton.hidden = false; + OptionsCancelButton.hidden = false; + OptionsDefaultsButton.hidden = false; +} + +function OptionsMenuOKButton::onClick(%this) +{ + //save the settings and then back out + + OptionsMenu.backOut(); +} + +function OptionsMenuCancelButton::onClick(%this) +{ + //we don't save, so go straight to backing out of the menu + OptionsMenu.backOut(); +} + +function OptionsMenuDefaultsButton::onClick(%this) +{ + //we don't save, so go straight to backing out of the menu + OptionsMenu.backOut(); +} + +function ControlsSettingsMenuButton::onClick(%this) +{ + OptionsMain.hidden = true; + ControlsMenu.hidden = false; + + KeyboardControlPanel.hidden = false; + MouseControlPanel.hidden = true; + + ControlsMenu.reload(); +} + +function GraphicsSettingsMenuButton::onClick(%this) +{ + OptionsMain.hidden = true; + GraphicsMenu.hidden = false; +} + +function CameraSettingsMenuButton::onClick(%this) +{ + OptionsMain.hidden = true; + CameraMenu.hidden = false; + + CameraMenu.loadSettings(); +} + +function AudioSettingsMenuButton::onClick(%this) +{ + OptionsMain.hidden = true; + AudioMenu.hidden = false; + AudioMenu.loadSettings(); +} + +function ScreenBrSettingsMenuButton::onClick(%this) +{ + OptionsMain.hidden = true; + ScreenBrightnessMenu.hidden = false; +} + +function OptionsMenu::backOut(%this) +{ + //save the settings and then back out + if(OptionsMain.hidden == false) + { + //we're not in a specific menu, so we're actually exiting + Canvas.popDialog(OptionsMenu); + + if(isObject(OptionsMenu.returnGui) && OptionsMenu.returnGui.isMethod("onReturnTo")) + OptionsMenu.returnGui.onReturnTo(); + } + else + { + OptionsMain.hidden = false; + ControlsMenu.hidden = true; + GraphicsMenu.hidden = true; + CameraMenu.hidden = true; + AudioMenu.hidden = true; + ScreenBrightnessMenu.hidden = true; + } +} + +function OptionsMenu::addSettingOption(%this, %arrayTarget) +{ + %graphicsOption = OptionsMenu.tamlReader.read("data/ui/scripts/guis/graphicsMenuSettingsCtrl.taml"); + + %arrayTarget.add(%graphicsOption); + + return %graphicsOption; +} + +function OptionsMenu::addSliderOption(%this, %arrayTarget, %range, %ticks, %variable, %value, %class) +{ + %graphicsOption = OptionsMenu.tamlReader.read("data/ui/scripts/guis/graphicsMenuSettingsSlider.taml"); + + %arrayTarget.add(%graphicsOption); + + if(%range !$= "") + { + %graphicsOption-->slider.range = %range; + } + + if(%ticks !$= "") + { + %graphicsOption-->slider.ticks = %ticks; + } + + if(%variable !$= "") + { + %graphicsOption-->slider.variable = %variable; + } + + if(%value !$= "") + { + %graphicsOption-->slider.setValue(%value); + } + + if(%class !$= "") + { + %graphicsOption-->slider.className = %class; + } + else + %graphicsOption-->slider.className = OptionsMenuSlider; + + %graphicsOption-->slider.snap = true; + + %graphicsOption-->slider.onValueSet(); + + return %graphicsOption; +} + +function OptionsMenuSlider::onMouseDragged(%this) +{ + %this.onValueSet(); +} + +function OptionsMenuSlider::onValueSet(%this) +{ + %this.getParent().getParent()-->valueText.setText(mRound(%this.value * 10)); +} + +function FOVOptionSlider::onMouseDragged(%this) +{ + %this.onValueSet(); +} + +function FOVOptionSlider::onValueSet(%this) +{ + %this.getParent().getParent()-->valueText.setText(mRound(%this.value)); +} + +/// Returns true if the current quality settings equal +/// this graphics quality level. +function OptionsMenuSettingLevel::isCurrent( %this ) +{ + // Test each pref to see if the current value + // equals our stored value. + + for ( %i=0; %i < %this.count(); %i++ ) + { + %pref = %this.getKey( %i ); + %value = %this.getValue( %i ); + + %prefVarValue = getVariable( %pref ); + if ( getVariable( %pref ) !$= %value ) + return false; + } + + return true; +} +// ============================================================================= +// CAMERA MENU +// ============================================================================= +function CameraMenu::onWake(%this) +{ + +} + +function CameraMenu::apply(%this) +{ + setFOV($pref::Player::defaultFov); +} + +function CameraMenu::loadSettings(%this) +{ + CameraMenuOptionsArray.clear(); + + %option = OptionsMenu.addSettingOption(CameraMenuOptionsArray); + %option-->nameText.setText("Invert Vertical"); + %option.qualitySettingGroup = InvertVerticalMouse; + %option.init(); + + %option = OptionsMenu.addSliderOption(CameraMenuOptionsArray, "0.1 1", 8, "$pref::Input::VertMouseSensitivity", $pref::Input::VertMouseSensitivity); + %option-->nameText.setText("Vertical Sensitivity"); + + %option = OptionsMenu.addSliderOption(CameraMenuOptionsArray, "0.1 1", 8, "$pref::Input::HorzMouseSensitivity", $pref::Input::HorzMouseSensitivity); + %option-->nameText.setText("Horizontal Sensitivity"); + + %option = OptionsMenu.addSliderOption(CameraMenuOptionsArray, "0.1 1", 8, "$pref::Input::ZoomVertMouseSensitivity", $pref::Input::ZoomVertMouseSensitivity); + %option-->nameText.setText("Zoom Vertical Sensitivity"); + + %option = OptionsMenu.addSliderOption(CameraMenuOptionsArray, "0.1 1", 8, "$pref::Input::ZoomHorzMouseSensitivity", $pref::Input::ZoomHorzMouseSensitivity); + %option-->nameText.setText("Zoom Horizontal Sensitivity"); + + %option = OptionsMenu.addSliderOption(CameraMenuOptionsArray, "65 90", 25, "$pref::Player::defaultFov", $pref::Player::defaultFov, FOVOptionSlider); + %option-->nameText.setText("Field of View"); + + CameraMenuOptionsArray.refresh(); +} + +function CameraMenuOKButton::onClick(%this) +{ + //save the settings and then back out + CameraMenu.apply(); + OptionsMenu.backOut(); +} + +function CameraMenuDefaultsButton::onClick(%this) +{ + +} +// ============================================================================= +// AUDIO MENU +// ============================================================================= +$AudioTestHandle = 0; +// Description to use for playing the volume test sound. This isn't +// played with the description of the channel that has its volume changed +// because we know nothing about the playback state of the channel. If it +// is paused or stopped, the test sound would not play then. +$AudioTestDescription = new SFXDescription() +{ + sourceGroup = AudioChannelMaster; +}; + +function AudioMenu::loadSettings(%this) +{ + // Audio + //OptAudioHardwareToggle.setStateOn($pref::SFX::useHardware); + //OptAudioHardwareToggle.setActive( true ); + + %this-->OptAudioVolumeMaster.setValue( $pref::SFX::masterVolume ); + %this-->OptAudioVolumeShell.setValue( $pref::SFX::channelVolume[ $GuiAudioType] ); + %this-->OptAudioVolumeSim.setValue( $pref::SFX::channelVolume[ $SimAudioType ] ); + %this-->OptAudioVolumeMusic.setValue( $pref::SFX::channelVolume[ $MusicAudioType ] ); + + AudioMenuSoundDriver.clear(); + %buffer = sfxGetAvailableDevices(); + %count = getRecordCount( %buffer ); + for(%i = 0; %i < %count; %i++) + { + %record = getRecord(%buffer, %i); + %provider = getField(%record, 0); + + if ( AudioMenuSoundDriver.findText( %provider ) == -1 ) + AudioMenuSoundDriver.add( %provider, %i ); + } + + AudioMenuSoundDriver.sort(); + + %selId = AudioMenuSoundDriver.findText($pref::SFX::provider); + if ( %selId == -1 ) + AudioMenuSoundDriver.setFirstSelected(); + else + AudioMenuSoundDriver.setSelected( %selId ); +} + +function AudioMenu::loadDevices(%this) +{ + if(!isObject(SoundDeviceGroup)) + { + new SimGroup( SoundDeviceGroup ); + } + else + { + SoundDeviceGroup.clear(); + } + + %buffer = sfxGetAvailableDevices(); + %count = getRecordCount( %buffer ); + for (%i = 0; %i < %count; %i++) + { + %record = getRecord(%buffer, %i); + %provider = getField(%record, 0); + %device = getField(%record, 1); + + if($pref::SFX::provider !$= %provider) + continue; + + %setting = new ArrayObject() + { + class = "OptionsMenuSettingLevel"; + caseSensitive = true; + + displayName = %device; + + key["$pref::SFX::Device"] = %device; + }; + + SoundDeviceGroup.add(%setting); + } +} + +function AudioMenu::apply(%this) +{ + sfxSetMasterVolume( $pref::SFX::masterVolume ); + + sfxSetChannelVolume( $GuiAudioType, $pref::SFX::channelVolume[ $GuiAudioType ] ); + sfxSetChannelVolume( $SimAudioType, $pref::SFX::channelVolume[ $SimAudioType ] ); + sfxSetChannelVolume( $MusicAudioType, $pref::SFX::channelVolume[ $MusicAudioType ] ); + + if ( !sfxCreateDevice( $pref::SFX::provider, + $pref::SFX::device, + $pref::SFX::useHardware, + -1 ) ) + error( "Unable to create SFX device: " @ $pref::SFX::provider + SPC $pref::SFX::device + SPC $pref::SFX::useHardware ); + + if( !isObject( $AudioTestHandle ) ) + { + sfxPlay(menuButtonPressed); + } +} + +function AudioMenuOKButton::onClick(%this) +{ + //save the settings and then back out + AudioMenu.apply(); + OptionsMenu.backOut(); +} + +function AudioMenuDefaultsButton::onClick(%this) +{ + sfxInit(); + AudioMenu.loadSettings(); +} + +function OptAudioUpdateMasterVolume( %volume ) +{ + if( %volume == $pref::SFX::masterVolume ) + return; + + sfxSetMasterVolume( %volume ); + $pref::SFX::masterVolume = %volume; + + if( !isObject( $AudioTestHandle ) ) + $AudioTestHandle = sfxPlayOnce( AudioChannel, "art/sound/ui/volumeTest.wav" ); +} + +function OptAudioUpdateChannelVolume( %description, %volume ) +{ + %channel = sfxGroupToOldChannel( %description.sourceGroup ); + + if( %volume == $pref::SFX::channelVolume[ %channel ] ) + return; + + sfxSetChannelVolume( %channel, %volume ); + $pref::SFX::channelVolume[ %channel ] = %volume; + + if( !isObject( $AudioTestHandle ) ) + { + $AudioTestDescription.volume = %volume; + $AudioTestHandle = sfxPlayOnce( $AudioTestDescription, "art/sound/ui/volumeTest.wav" ); + } +} + +function AudioMenuSoundDriver::onSelect( %this, %id, %text ) +{ + // Skip empty provider selections. + if ( %text $= "" ) + return; + + $pref::SFX::provider = %text; + AudioMenuSoundDevice.clear(); + + %buffer = sfxGetAvailableDevices(); + %count = getRecordCount( %buffer ); + for(%i = 0; %i < %count; %i++) + { + %record = getRecord(%buffer, %i); + %provider = getField(%record, 0); + %device = getField(%record, 1); + + if (%provider !$= %text) + continue; + + if ( AudioMenuSoundDevice.findText( %device ) == -1 ) + AudioMenuSoundDevice.add( %device, %i ); + } + + // Find the previous selected device. + %selId = AudioMenuSoundDevice.findText($pref::SFX::device); + if ( %selId == -1 ) + AudioMenuSoundDevice.setFirstSelected(); + else + AudioMenuSoundDevice.setSelected( %selId ); +} + +function AudioMenuSoundDevice::onSelect( %this, %id, %text ) +{ + // Skip empty selections. + if ( %text $= "" ) + return; + + $pref::SFX::device = %text; + + if ( !sfxCreateDevice( $pref::SFX::provider, + $pref::SFX::device, + $pref::SFX::useHardware, + -1 ) ) + error( "Unable to create SFX device: " @ $pref::SFX::provider + SPC $pref::SFX::device + SPC $pref::SFX::useHardware ); +} diff --git a/Templates/BaseGame/game/data/ui/scripts/pauseMenu.cs b/Templates/BaseGame/game/data/ui/scripts/pauseMenu.cs new file mode 100644 index 000000000..b23b423f0 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/pauseMenu.cs @@ -0,0 +1,30 @@ +function PauseMenu::onWake(%this) +{ + $timescale = 0; +} + +function PauseMenu::onSleep(%this) +{ + $timescale = 1; +} + +function PauseMenu::openOptionsMenu(%this) +{ + Canvas.pushDialog(OptionsMenu); + OptionsMenu.returnGui = %this; + PauseOptionsMain.hidden = true; +} + +function PauseMenu::openControlsMenu(%this) +{ + Canvas.pushDialog(OptionsMenu); + OptionsMenu.returnGui = %this; + PauseOptionsMain.hidden = true; + OptionsMain.hidden = true; + ControlsMenu.hidden = false; +} + +function PauseMenu::onReturnTo(%this) +{ + PauseOptionsMain.hidden = false; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/scripts/profiles.cs b/Templates/BaseGame/game/data/ui/scripts/profiles.cs new file mode 100644 index 000000000..5e6303fb0 --- /dev/null +++ b/Templates/BaseGame/game/data/ui/scripts/profiles.cs @@ -0,0 +1,429 @@ +if( !isObject( GuiMenuButtonProfile ) ) +new GuiControlProfile( GuiMenuButtonProfile ) +{ + opaque = true; + border = false; + fontSize = 18; + fontType = "Arial Bold"; + fontColor = "240 240 240"; + fontColorHL = "0 0 0"; + fontColorNA = "125 125 125"; + //fontColorSEL ="0 0 0"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "data/ui/art/menu-button"; + hasBitmapArray = false; + soundButtonDown = menuButtonPressed; + soundButtonOver = menuButtonHover; + category = "Core"; +}; + +if( !isObject( GuiHighlightMenuButtonProfile ) ) +new GuiControlProfile( GuiHighlightMenuButtonProfile ) +{ + opaque = true; + border = false; + fontSize = 18; + fontType = "Arial Bold"; + fontColor = "240 240 240"; + fontColorHL = "0 0 0"; + fontColorNA = "125 125 125"; + //fontColorSEL ="0 0 0"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "data/ui/art/selector-button-highlight-only"; + hasBitmapArray = false; + category = "Core"; +}; + +if( !isObject( GuiBlankMenuButtonProfile ) ) +new GuiControlProfile( GuiBlankMenuButtonProfile ) +{ + opaque = true; + border = false; + fontSize = 18; + fontType = "Arial Bold"; + fontColor = "200 200 200"; + fontColorHL = "255 255 255"; + fontColorNA = "200 200 200"; + //fontColorSEL ="0 0 0"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "data/ui/art/selector-button-blank"; + hasBitmapArray = false; + soundButtonDown = menuButtonPressed; + soundButtonOver = menuButtonHover; + category = "Core"; +}; + +if( !isObject( GuiMenuTextProfile ) ) +new GuiControlProfile( GuiMenuTextProfile ) +{ + opaque = true; + border = false; + fontSize = 18; + fontType = "Arial Bold"; + fontColor = "240 240 240"; + fontColorHL = "0 0 0"; + fontColorNA = "125 125 125"; + fixedExtent = false; + justify = "center"; + category = "Core"; +}; + +if( !isObject( GuiSolidDefaultProfile ) ) +new GuiControlProfile (GuiSolidDefaultProfile) +{ + opaque = true; + border = true; + category = "Core"; +}; + +if( !isObject( GuiTransparentProfile ) ) +new GuiControlProfile (GuiTransparentProfile) +{ + opaque = false; + border = false; + category = "Core"; +}; + +if( !isObject( GuiGroupBorderProfile ) ) +new GuiControlProfile( GuiGroupBorderProfile ) +{ + border = false; + opaque = false; + hasBitmapArray = true; + bitmap = "data/ui/art/group-border"; + category = "Core"; +}; + +if( !isObject( GuiTabBorderProfile ) ) +new GuiControlProfile( GuiTabBorderProfile ) +{ + border = false; + opaque = false; + hasBitmapArray = true; + bitmap = "data/ui/art/tab-border"; + category = "Core"; +}; + +if( !isObject( GuiModelessDialogProfile ) ) +new GuiControlProfile( GuiModelessDialogProfile ) +{ + modal = false; + category = "Core"; +}; + +if( !isObject( GuiFrameSetProfile ) ) +new GuiControlProfile (GuiFrameSetProfile) +{ + fillcolor = "255 255 255"; + borderColor = "246 245 244"; + border = 1; + opaque = true; + border = true; + category = "Core"; +}; + +if( !isObject( GuiInputCtrlProfile ) ) +new GuiControlProfile( GuiInputCtrlProfile ) +{ + tab = true; + canKeyFocus = true; + category = "Core"; +}; + +if( !isObject( GuiTextProfile ) ) +new GuiControlProfile (GuiTextProfile) +{ + justify = "left"; + fontColor = "20 20 20"; + category = "Core"; +}; + +if( !isObject( GuiTextRightProfile ) ) +new GuiControlProfile (GuiTextRightProfile : GuiTextProfile) +{ + justify = "right"; + category = "Core"; +}; + +if( !isObject( GuiAutoSizeTextProfile ) ) +new GuiControlProfile (GuiAutoSizeTextProfile) +{ + fontColor = "0 0 0"; + autoSizeWidth = true; + autoSizeHeight = true; + category = "Core"; +}; + +if( !isObject( GuiMediumTextProfile ) ) +new GuiControlProfile( GuiMediumTextProfile : GuiTextProfile ) +{ + fontSize = 24; + category = "Core"; +}; + +if( !isObject( GuiBigTextProfile ) ) +new GuiControlProfile( GuiBigTextProfile : GuiTextProfile ) +{ + fontSize = 36; + category = "Core"; +}; + +if( !isObject( GuiMLTextProfile ) ) +new GuiControlProfile( GuiMLTextProfile ) +{ + fontColorLink = "100 100 100"; + fontColorLinkHL = "255 255 255"; + autoSizeWidth = true; + autoSizeHeight = true; + border = false; + category = "Core"; +}; + +if( !isObject( GuiMLWhiteTextProfile ) ) +new GuiControlProfile( GuiMLWhiteTextProfile ) +{ + fontColor = "220 220 220"; + fontColorHL = "255 255 255"; + autoSizeWidth = true; + autoSizeHeight = true; + border = false; + category = "Core"; +}; + +if( !isObject( GuiTextArrayProfile ) ) +new GuiControlProfile( GuiTextArrayProfile : GuiTextProfile ) +{ + fontColor = "250 250 250"; + fontColorHL = " 0 0 0"; + fontColorSEL = "0 0 0"; + fillColor ="50 50 50"; + fillColorHL = "125 125 125"; + fillColorSEL = "180 180 180"; + border = false; + category = "Core"; +}; + +// ---------------------------------------------------------------------------- +// TODO: Revisit Popupmenu +// ---------------------------------------------------------------------------- + +if( !isObject( GuiPopupMenuItemBorder ) ) +new GuiControlProfile( GuiPopupMenuItemBorder : GuiButtonProfile ) +{ + opaque = true; + border = true; + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorNA = "255 255 255"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "data/ui/art/button"; + category = "Core"; +}; + +if( !isObject( GuiPopUpMenuDefault ) ) +new GuiControlProfile( GuiPopUpMenuDefault : GuiDefaultProfile ) +{ + opaque = true; + mouseOverSelected = true; + textOffset = "3 3"; + border = 0; + borderThickness = 0; + fixedExtent = true; + bitmap = "data/ui/art/scrollBar"; + hasBitmapArray = true; + profileForChildren = GuiPopupMenuItemBorder; + fillColor = "242 241 240 ";//"255 255 255";//100 + fillColorHL = "228 228 235 ";//"204 203 202"; + fillColorSEL = "98 100 137 ";//"204 203 202"; + // font color is black + fontColorHL = "0 0 0 ";//"0 0 0"; + fontColorSEL = "255 255 255";//"0 0 0"; + borderColor = "100 100 100"; + category = "Core"; +}; + +if( !isObject( GuiPopUpMenuProfile ) ) +new GuiControlProfile( GuiPopUpMenuProfile : GuiPopUpMenuDefault ) +{ + textOffset = "6 4"; + bitmap = "data/ui/art/dropDown"; + hasBitmapArray = true; + border = 1; + profileForChildren = GuiPopUpMenuDefault; + category = "Core"; +}; + +if( !isObject( GuiTabBookProfile ) ) +new GuiControlProfile( GuiTabBookProfile ) +{ + fillColorHL = "100 100 100"; + fillColorNA = "150 150 150"; + fontColor = "30 30 30"; + fontColorHL = "0 0 0"; + fontColorNA = "50 50 50"; + fontType = "Arial"; + fontSize = 14; + justify = "center"; + bitmap = "data/ui/art/tab"; + tabWidth = 64; + tabHeight = 24; + tabPosition = "Top"; + tabRotation = "Horizontal"; + textOffset = "0 -3"; + tab = true; + cankeyfocus = true; + category = "Core"; +}; + +if( !isObject( GuiTabPageProfile ) ) +new GuiControlProfile( GuiTabPageProfile : GuiDefaultProfile ) +{ + fontType = "Arial"; + fontSize = 10; + justify = "center"; + bitmap = "data/ui/art/tab"; + opaque = false; + fillColor = "240 239 238"; + category = "Core"; +}; + +if( !isObject( GuiConsoleProfile ) ) +new GuiControlProfile( GuiConsoleProfile ) +{ + fontType = ($platform $= "macos") ? "Monaco" : "Lucida Console"; + fontSize = ($platform $= "macos") ? 13 : 12; + fontColor = "255 255 255"; + fontColorHL = "0 255 255"; + fontColorNA = "255 0 0"; + fontColors[6] = "100 100 100"; + fontColors[7] = "100 100 0"; + fontColors[8] = "0 0 100"; + fontColors[9] = "0 100 0"; + category = "Core"; +}; + +if( !isObject( GuiConsoleTextProfile ) ) +new GuiControlProfile( GuiConsoleTextProfile ) +{ + fontColor = "0 0 0"; + autoSizeWidth = true; + autoSizeHeight = true; + textOffset = "2 2"; + opaque = true; + fillColor = "255 255 255"; + border = true; + borderThickness = 1; + borderColor = "0 0 0"; + category = "Core"; +}; + +$ConsoleDefaultFillColor = "0 0 0 175"; + +if( !isObject( ConsoleScrollProfile ) ) +new GuiControlProfile( ConsoleScrollProfile : GuiScrollProfile ) +{ + opaque = true; + fillColor = $ConsoleDefaultFillColor; + border = 1; + //borderThickness = 0; + borderColor = "0 0 0"; + category = "Core"; +}; + +if( !isObject( ConsoleTextEditProfile ) ) +new GuiControlProfile( ConsoleTextEditProfile : GuiTextEditProfile ) +{ + fillColor = "242 241 240 255"; + fillColorHL = "255 255 255"; + category = "Core"; +}; + +//----------------------------------------------------------------------------- +// Center and bottom print +//----------------------------------------------------------------------------- + +if( !isObject( CenterPrintProfile ) ) +new GuiControlProfile ( CenterPrintProfile ) +{ + opaque = false; + fillColor = "128 128 128"; + fontColor = "0 255 0"; + border = true; + borderColor = "0 255 0"; + category = "Core"; +}; + +if( !isObject( CenterPrintTextProfile ) ) +new GuiControlProfile ( CenterPrintTextProfile ) +{ + opaque = false; + fontType = "Arial"; + fontSize = 12; + fontColor = "0 255 0"; + category = "Core"; +}; + +// ---------------------------------------------------------------------------- +// Radio button control +// ---------------------------------------------------------------------------- + +if( !isObject( GuiRadioProfile ) ) +new GuiControlProfile( GuiRadioProfile ) +{ + fontSize = 14; + fillColor = "232 232 232"; + fontColor = "20 20 20"; + fontColorHL = "80 80 80"; + fixedExtent = true; + bitmap = "data/ui/art/radioButton"; + hasBitmapArray = true; + category = "Core"; +}; + +// --------------------------------------------------------------------------- +// Slider control +// --------------------------------------------------------------------------- +if( !isObject( GuiSliderProfile ) ) +new GuiControlProfile( GuiSliderProfile ) +{ + bitmap = "data/ui/art/slider"; + category = "Core"; +}; + +// +// Scroll Profile +// +if(!isObject(GuiMenuScrollProfile)) +new GuiControlProfile(GuiMenuScrollProfile) +{ + opaque = true; + fillcolor = "50 50 50"; + fontColor = "200 200 200"; + fontColorHL = "250 250 250"; + border = true; + bitmap = "data/ui/art/scrollBar"; + hasBitmapArray = true; + category = "Core"; +}; + +// Scroll +if(!isObject(GuiMenuScrollProfile)) +new GuiControlProfile(GuiMenuScrollProfile) +{ + opaque = true; + fillcolor = "128 128 128"; + fontColor = "0 0 0"; + fontColorHL = "150 150 150"; + border = true; + bitmap = "./images/scrollBar"; + hasBitmapArray = true; + category = "Core"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/data/ui/sound/buttonClick.wav b/Templates/BaseGame/game/data/ui/sound/buttonClick.wav new file mode 100644 index 0000000000000000000000000000000000000000..ec55432ee85d2707bde59d237c8e3c3a59a42508 GIT binary patch literal 16596 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 HiVOb_-%tp| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/data/ui/sound/buttonHover.wav b/Templates/BaseGame/game/data/ui/sound/buttonHover.wav new file mode 100644 index 0000000000000000000000000000000000000000..e7276cde480736bd1170133f920fb207c587fb63 GIT binary patch literal 40136 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/main.cs.in b/Templates/BaseGame/game/main.cs.in new file mode 100644 index 000000000..760e45b02 --- /dev/null +++ b/Templates/BaseGame/game/main.cs.in @@ -0,0 +1,93 @@ +$Core::windowIcon = "data/icon.png"; +$Core::splashWindowImage = "data/splash.png"; + +// Display a splash window immediately to improve app responsiveness before +// engine is initialized and main window created. +displaySplashWindow($Core::splashWindowImage); + +// Console does something. +setLogMode(6); + +// Disable script trace. +trace(false); + +// Set the name of our application +$appName = "@TORQUE_APP_NAME@"; + +//----------------------------------------------------------------------------- +// Load up scripts to initialise subsystems. +exec("core/main.cs"); + +// Parse the command line arguments +echo("\n--------- Parsing Arguments ---------"); +parseArgs(); + +// The canvas needs to be initialized before any gui scripts are run since +// some of the controls assume that the canvas exists at load time. +createCanvas($appName); + +//----------------------------------------------------------------------------- +// Load console. +exec("core/console/main.cs"); + +// Init the physics plugin. +physicsInit(); + +sfxStartup(); + +// Set up networking. +setNetPort(0); + +// Start processing file change events. +startFileChangeNotifications(); + +// If we have editors, initialize them here as well +if(isFile("tools/main.cs") && !$isDedicated) + exec("tools/main.cs"); + +ModuleDatabase.setModuleExtension("module"); +ModuleDatabase.scanModules( "data", false ); +ModuleDatabase.LoadGroup( "Game" ); + +if( !$isDedicated ) +{ + // Start rendering and stuff. + initRenderManager(); + initLightingSystems("Advanced Lighting"); + + //load prefs + %prefPath = getPrefpath(); + if ( isFile( %prefPath @ "/clientPrefs.cs" ) ) + exec( %prefPath @ "/clientPrefs.cs" ); + else + exec("data/defaults.cs"); + + configureCanvas(); + + //Autodetect settings if it's our first time + if($pref::Video::autoDetect) + GraphicsMenu.Autodetect(); + + closeSplashWindow(); + + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + Canvas.showWindow(); +} +else +{ + closeSplashWindow(); +} + +echo("Engine initialized..."); + +//----------------------------------------------------------------------------- +// Called when the engine is shutting down. +function onExit() +{ + // Stop file change events. + stopFileChangeNotifications(); + + ModuleDatabase.UnloadExplicit( "Game" ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/runTests.cs b/Templates/BaseGame/game/runTests.cs new file mode 100644 index 000000000..b6d903ff0 --- /dev/null +++ b/Templates/BaseGame/game/runTests.cs @@ -0,0 +1,5 @@ +setLogMode(2); +$Con::LogBufferEnabled = false; +$Testing::CheckMemoryLeaks = false; +runAllUnitTests("-*.Stress*"); +quit(); diff --git a/Templates/BaseGame/game/tools/base/canvas/baseCanvas.ed.cs b/Templates/BaseGame/game/tools/base/canvas/baseCanvas.ed.cs new file mode 100644 index 000000000..4689d0ae5 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/canvas/baseCanvas.ed.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +function BaseEditorCanvas::onAdd( %this ) +{ + %this.createMenuBar(); + + %panel = new GuiPanel() { internalName = "DocumentContainer"; }; + %this.setContent( %panel ); + + %xOffset = 20; + %yOffset = 20; + + for( %i =0; %i<10; %i++ ) + { + %window = new GuiWindowCtrl() + { + extent = "200 100"; + position = %xOffset SPC %yOffset; + }; + %panel.add( %window ); + + %xOffset += 30; + %yOffset += 30; + + } +} + +function BaseEditorCanvas::onRemove( %this ) +{ + %this.destroyMenuBar(); +} + +function testBaseEditor() +{ + %baseEd = new GuiCanvas() { class="BaseEditorCanvas"; }; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/base/main.cs b/Templates/BaseGame/game/tools/base/main.cs new file mode 100644 index 000000000..eab511eb6 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/main.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeBase() +{ + echo(" % - Initializing Base Editor"); + + // Load Custom Editors + loadDirectory( expandFilename( "./canvas" ) ); + loadDirectory( expandFilename( "./menuBar" ) ); + loadDirectory( expandFilename( "./utils" ) ); +} + +function destroyBase() +{ +} diff --git a/Templates/BaseGame/game/tools/base/menuBar/baseMenu.ed.cs b/Templates/BaseGame/game/tools/base/menuBar/baseMenu.ed.cs new file mode 100644 index 000000000..f53606518 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/menuBar/baseMenu.ed.cs @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// onAdd creates the base menu's and document controller +function BaseEditorCanvas::createMenuBar( %this ) +{ + if(isObject(%this.menuBar)) + return; + + // Menu bar + %this.menuBar = new MenuBar() + { + dynamicItemInsertPos = 3; + + // File Menu + new PopupMenu() + { + superClass = "MenuBuilder"; + class = "BaseEditorFileMenu"; + internalName = "FileMenu"; + + barTitle = "File"; + + item[0] = "New..." TAB "Ctrl N" TAB "[this].onNew();"; + item[1] = "Open..." TAB "Ctrl O" TAB "[this].onOpen();"; + item[2] = "-"; + item[3] = "Save" TAB "Ctrl S" TAB "[this].onSave();"; + item[4] = "Save As" TAB "Ctrl-Alt S" TAB "[this].onSaveAs();"; + item[5] = "Save All" TAB "Ctrl-Shift S" TAB "[this].onSaveAll();"; + item[6] = "-"; + item[7] = "Import..." TAB "Ctrl-Shift I" TAB "[this].onImport();"; + item[8] = "Export..." TAB "Ctrl-Shift E" TAB "[this].onExport();"; + item[9] = "-"; + item[10] = "Revert" TAB "Ctrl R" TAB "[this].onRevert();"; + item[11] = "-"; + item[12] = "Close" TAB "Ctrl W" TAB "[this].onClose();"; + }; + }; +} + +function BaseEditorCanvas::destroyMenuBar( %this ) +{ + if( isObject( %this.menuBar ) ) + %this.menuBar.delete(); +} + +function BaseEditorCanvas::onCreateMenu(%this) +{ + if( !isObject( %this.menuBar ) ) + %this.createMenuBar(); + + %this.menuBar.attachToCanvas( %this, 0 ); +} + +function BaseEditorCanvas::onDestroyMenu(%this) +{ + if( isObject( %this.menuBar ) ) + { + %this.destroyMenuBar(); + %this.menuBar.removeFromCanvas( %this ); + } +} diff --git a/Templates/BaseGame/game/tools/base/menuBar/fileMenu.ed.cs b/Templates/BaseGame/game/tools/base/menuBar/fileMenu.ed.cs new file mode 100644 index 000000000..1ab685e4a --- /dev/null +++ b/Templates/BaseGame/game/tools/base/menuBar/fileMenu.ed.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// +// +// +function BaseEditorFileMenu::onNew( %this ) +{ +} +function BaseEditorFileMenu::onOpen( %this ) +{ +} +function BaseEditorFileMenu::onSave( %this ) +{ +} +function BaseEditorFileMenu::onSaveAs( %this ) +{ +} +function BaseEditorFileMenu::onSaveAll( %this ) +{ +} +function BaseEditorFileMenu::onRevert( %this ) +{ +} +function BaseEditorFileMenu::onClose( %this ) +{ +} +function BaseEditorFileMenu::onImport( %this ) +{ +} +function BaseEditorFileMenu::onExport( %this ) +{ +} diff --git a/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.cs b/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.cs new file mode 100644 index 000000000..11d66b2ed --- /dev/null +++ b/Templates/BaseGame/game/tools/base/menuBar/menuBuilder.ed.cs @@ -0,0 +1,248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Menu Builder Helper Class +//----------------------------------------------------------------------------- +/// @class MenuBuilder +/// @brief Create Dynamic Context and MenuBar Menus +/// +/// +/// Summary : The MenuBuilder script class exists merely as a helper for creating +/// popup menu's for use in torque editors. It is setup as a single +/// object with dynamic fields starting with item[0]..[n] that describe +/// how to create the menu in question. An example is below. +/// +/// isPopup : isPopup is a persistent field on PopupMenu console class which +/// when specified to true will allow you to perform .showPopup(x,y) +/// commands which allow popupmenu's to be used/reused as menubar menus +/// as well as context menus. +/// +/// barPosition : barPosition indicates which index on the menu bar (0 = leftmost) +/// to place this menu if it is attached. Use the attachToMenuBar() command +/// to attach a menu. +/// +/// barName : barName specifies the visible name of a menu item that is attached +/// to the global menubar. +/// +/// canvas : The GuiCanvas object the menu should be attached to. This defaults to +/// the global Canvas object if unspecified. +/// +/// Remarks : If you wish to use a menu as a context popup menu, isPopup must be +/// specified as true at the creation time of the menu. +/// +/// +/// @li @b item[n] (String) TAB (String) TAB (String) : A Menu Item Definition. +/// @code item[0] = "Open File..." TAB "Ctrl O" TAB "Something::OpenFile"; @endcode +/// +/// @li @b isPopup (bool) : If Specified the menu will be considered a popup menu and should be used via .showPopup() +/// @code isPopup = true; @endcode +/// +/// +/// Example : Creating a @b MenuBar Menu +/// @code +/// %%editMenu = new PopupMenu() +/// { +/// barPosition = 3; +/// barName = "View"; +/// superClass = "MenuBuilder"; +/// item[0] = "Undo" TAB "Ctrl Z" TAB "levelBuilderUndo(1);"; +/// item[1] = "Redo" TAB "Ctrl Y" TAB "levelBuilderRedo(1);"; +/// item[2] = "-"; +/// }; +/// +/// %%editMenu.attachToMenuBar( 1, "Edit" ); +/// +/// @endcode +/// +/// +/// Example : Creating a @b Context (Popup) Menu +/// @code +/// %%contextMenu = new PopupMenu() +/// { +/// superClass = MenuBuilder; +/// isPopup = true; +/// item[0] = "My Super Cool Item" TAB "Ctrl 2" TAB "echo(\"Clicked Super Cool Item\");"; +/// item[1] = "-"; +/// }; +/// +/// %%contextMenu.showPopup(); +/// @endcode +/// +/// +/// Example : Modifying a Menu +/// @code +/// %%editMenu = new PopupMenu() +/// { +/// item[0] = "Foo" TAB "Ctrl F" TAB "echo(\"clicked Foo\")"; +/// item[1] = "-"; +/// }; +/// %%editMenu.addItem( 2, "Bar" TAB "Ctrl B" TAB "echo(\"clicked Bar\")" ); +/// %%editMenu.removeItem( 0 ); +/// %%editMenu.addItem( 0, "Modified Foo" TAB "Ctrl F" TAB "echo(\"clicked modified Foo\")" ); +/// @endcode +/// +/// +/// @see PopupMenu +/// +//----------------------------------------------------------------------------- + +// Adds one item to the menu. +// if %item is skipped or "", we will use %item[#], which was set when the menu was created. +// if %item is provided, then we update %item[#]. +function MenuBuilder::addItem(%this, %pos, %item) +{ + if(%item $= "") + %item = %this.item[%pos]; + + if(%item !$= %this.item[%pos]) + %this.item[%pos] = %item; + + %name = getField(%item, 0); + %accel = getField(%item, 1); + %cmd = getField(%item, 2); + + // We replace the [this] token with our object ID + %cmd = strreplace( %cmd, "[this]", %this ); + %this.item[%pos] = setField( %item, 2, %cmd ); + + if(isObject(%accel)) + { + // If %accel is an object, we want to add a sub menu + %this.insertSubmenu(%pos, %name, %accel); + } + else + { + %this.insertItem(%pos, %name !$= "-" ? %name : "", %accel, %cmd); + } +} + +function MenuBuilder::appendItem(%this, %item) +{ + %this.addItem(%this.getItemCount(), %item); +} + +function MenuBuilder::onAdd(%this) +{ + if(! isObject(%this.canvas)) + %this.canvas = Canvas; + + for(%i = 0;%this.item[%i] !$= "";%i++) + { + %this.addItem(%i); + } +} + +function MenuBuilder::onRemove(%this) +{ + %this.removeFromMenuBar(); +} + +////////////////////////////////////////////////////////////////////////// + +function MenuBuilder::onSelectItem(%this, %id, %text) +{ + %cmd = getField(%this.item[%id], 2); + if(%cmd !$= "") + { + eval( %cmd ); + return true; + } + return false; +} + +/// Sets a new name on an existing menu item. +function MenuBuilder::setItemName( %this, %id, %name ) +{ + %item = %this.item[%id]; + %accel = getField(%item, 1); + %this.setItem( %id, %name, %accel ); +} + +/// Sets a new command on an existing menu item. +function MenuBuilder::setItemCommand( %this, %id, %command ) +{ + %this.item[%id] = setField( %this.item[%id], 2, %command ); +} + +/// (SimID this) +/// Wraps the attachToMenuBar call so that it does not require knowledge of +/// barName or barIndex to be removed/attached. This makes the individual +/// MenuBuilder items very easy to add and remove dynamically from a bar. +/// +function MenuBuilder::attachToMenuBar( %this ) +{ + if( %this.barName $= "" ) + { + error("MenuBuilder::attachToMenuBar - Menu property 'barName' not specified."); + return false; + } + + if( %this.barPosition < 0 ) + { + error("MenuBuilder::attachToMenuBar - Menu " SPC %this.barName SPC "property 'barPosition' is invalid, must be zero or greater."); + return false; + } + + Parent::attachToMenuBar( %this, %this.canvas, %this.barPosition, %this.barName ); +} + +////////////////////////////////////////////////////////////////////////// + +// Callbacks from PopupMenu. These callbacks are now passed on to submenus +// in C++, which was previously not the case. Thus, no longer anything to +// do in these. I am keeping the callbacks in case they are needed later. + +function MenuBuilder::onAttachToMenuBar(%this, %canvas, %pos, %title) +{ +} + +function MenuBuilder::onRemoveFromMenuBar(%this, %canvas) +{ +} + +////////////////////////////////////////////////////////////////////////// + +/// Method called to setup default state for the menu. Expected to be overriden +/// on an individual menu basis. See the mission editor for an example. +function MenuBuilder::setupDefaultState(%this) +{ + for(%i = 0;%this.item[%i] !$= "";%i++) + { + %name = getField(%this.item[%i], 0); + %accel = getField(%this.item[%i], 1); + %cmd = getField(%this.item[%i], 2); + + // Pass on to sub menus + if(isObject(%accel)) + %accel.setupDefaultState(); + } +} + +/// Method called to easily enable or disable all items in a menu. +function MenuBuilder::enableAllItems(%this, %enable) +{ + for(%i = 0; %this.item[%i] !$= ""; %i++) + { + %this.enableItem(%i, %enable); + } +} diff --git a/Templates/BaseGame/game/tools/base/utils/inspector.ed.cs b/Templates/BaseGame/game/tools/base/utils/inspector.ed.cs new file mode 100644 index 000000000..f7c27ecf0 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/utils/inspector.ed.cs @@ -0,0 +1,289 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Functionality that allows all editor inspectors to share certain functionality. + + + +//--------------------------------------------------------------------------------------------- + +function EditorInspectorBase::onAdd( %this ) +{ + if( !isObject( EditorInspectorBaseDatablockFieldPopup ) ) + new PopupMenu( EditorInspectorBaseDatablockFieldPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Edit Datablock" TAB "" TAB "DatablockEditorPlugin.openDatablock( %this.inspectorField.getData() );"; + Item[ 1 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenDeclarationInTorsion( %this.inspectorField.getData() );"; + item[ 2 ] = "Inspect Object" TAB "" TAB "inspectObject( %this.inspectorField.getData() );"; + item[ 3 ] = "-"; + item[ 4 ] = "Copy Value" TAB "" TAB "setClipboard( %this.inspectorField.getData() );"; + item[ 5 ] = "Paste Value" TAB "" TAB "%this.inspectorField.apply( getClipboard() );"; + item[ 6 ] = "Reset to Default" TAB "" TAB "%this.inspectorField.reset();"; + + inspectorField = -1; + }; + + if( !isObject( EditorInspectorBaseFieldPopup ) ) + new PopupMenu( EditorInspectorBaseFieldPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Inspect Object" TAB "" TAB "inspectObject( %this.inspectorField.getData() );"; + Item[ 1 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenDeclarationInTorsion( %this.inspectorField.getData() );"; + item[ 2 ] = "-"; + item[ 3 ] = "Copy Value" TAB "" TAB "setClipboard( %this.inspectorField.getData() );"; + item[ 4 ] = "Paste Value" TAB "" TAB "%this.inspectorField.apply( getClipboard() );"; + item[ 5 ] = "Reset to Default" TAB "" TAB "%this.inspectorField.reset();"; + + inspectorField = -1; + }; + + if( !isObject( EditorInspectorBaseFileFieldPopup ) ) + new PopupMenu( EditorInspectorBaseFileFieldPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Open File" TAB "" TAB "openFile( %this.filePath );"; + item[ 1 ] = "Open Folder" TAB "" TAB "openFolder( %this.folderPath );"; + item[ 2 ] = "-"; + item[ 3 ] = "Copy Value" TAB "" TAB "setClipboard( %this.inspectorField.getData() );"; + item[ 4 ] = "Paste Value" TAB "" TAB "%this.inspectorField.apply( getClipboard() );"; + item[ 5 ] = "Reset to Default" TAB "" TAB "%this.inspectorField.reset();"; + + inspectorField = -1; + folderPath = ""; + filePath = ""; + }; + + if( !isObject( EditorInspectorBaseShapeFieldPopup ) ) + new PopupMenu( EditorInspectorBaseShapeFieldPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Edit Shape" TAB "" TAB "ShapeEditorPlugin.openShape( %this.inspectorField.getData() );"; + item[ 1 ] = "-"; + item[ 2 ] = "Open File" TAB "" TAB "openFile( %this.filePath );"; + item[ 3 ] = "Open Folder" TAB "" TAB "openFolder( %this.folderPath );"; + item[ 4 ] = "-"; + item[ 5 ] = "Copy Value" TAB "" TAB "setClipboard( %this.inspectorField.getData() );"; + item[ 6 ] = "Paste Value" TAB "" TAB "%this.inspectorField.apply( getClipboard() );"; + item[ 7 ] = "Reset to Default" TAB "" TAB "%this.inspectorField.reset();"; + + inspectorField = -1; + folderPath = ""; + filePath = ""; + }; + + if( !isObject( EditorInspectorBaseProfileFieldPopup ) ) + new PopupMenu( EditorInspectorBaseProfileFieldPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Edit Profile" TAB "" TAB "if( !GuiEditorIsActive() ) toggleGuiEditor( true ); GuiEditor.editProfile( %this.inspectorField.getData() );"; + item[ 1 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenDeclarationInTorsion( %this.inspectorField.getData() );"; + item[ 2 ] = "Inspect Object" TAB "" TAB "inspectObject( %this.inspectorField.getData() );"; + item[ 3 ] = "-"; + item[ 4 ] = "Copy Value" TAB "" TAB "setClipboard( %this.inspectorField.getData() );"; + item[ 5 ] = "Paste Value" TAB "" TAB "%this.inspectorField.apply( getClipboard() );"; + item[ 6 ] = "Reset to Default" TAB "" TAB "%this.inspectorField.reset();"; + + inspectorField = -1; + folderPath = ""; + filePath = ""; + }; +} + +//--------------------------------------------------------------------------------------------- + +function EditorInspectorBase::onFieldRightClick( %this, %field ) +{ + %obj = %this.getInspectObject(); + %fieldValue = %field.getData(); + + %inspectIndex = -1; + %openFileIndex = -1; + %openFolderIndex = -1; + + // Find out if this is a TypeFilename field referring to a shape file. + + %isShapeFilenameField = false; + if( %field.getInspectedFieldName() $= "shapeName" ) + { + %isShapeFilenameField = + %obj.isMemberOfClass( "PhysicsShape" ) || + %obj.isMemberOfClass( "TSStatic" ); + } + else if( %field.getInspectedFieldName() $= "shapeFile" ) + { + %isShapeFilenameField = + %obj.isMemberOfClass( "ShapeBaseData" ) || + %obj.isMemberOfClass( "ShapeBaseImageData" ) || + %obj.isMemberOfClass( "ForestItemData" ) || + %obj.isMemberOfClass( "WheeledVehicleTire" ) || + %obj.isMemberOfClass( "fxShapeReplicator" ) || + %obj.isMemberOfClass( "RenderShapeExample" ) || + %obj.isMemberOfClass( "DebrisData" ); + } + + // Select the popup. + + if( %isShapeFilenameField ) + { + %popup = EditorInspectorBaseShapeFieldPopup; + + %openFileIndex = 2; + %openFolderIndex = 3; + } + else if( EditorInspectorBase::isFileTypeField( %field ) ) + { + %popup = EditorInspectorBaseFileFieldPopup; + %openFileIndex = 0; + %openFolderIndex = 1; + } + else + { + switch$( %field.getClassName() ) + { + case "GuiInspectorCustomField": + if( %field.getInspectedFieldName() !$= "parentGroup" ) + return; + + case "GuiInspectorTypeGuiProfile": + + %popup = EditorInspectorBaseProfileFieldPopup; + + %popup.enableItem( 0, isObject( %fieldValue ) ); + %inspectIndex = 2; + %jumpToIndex = 1; + + case "GuiInspectorDatablockField" or + "GuiInspectorTypeSFXDescriptionName" or + "GuiInspectorTypeSFXEnvironmentName" or + "GuiInspectorTypeSFXTrackName" or + "GuiInspectorTypeSFXAmbienceName" or + "GuiInspectorTypeSFXSourceName": + + %popup = EditorInspectorBaseDatablockFieldPopup; + %popup.enableItem( 0, isObject( %fieldValue ) ); + %inspectIndex = 2; + %jumpToIndex = 1; + + default: + + %popup = EditorInspectorBaseFieldPopup; + %inspectIndex = 0; + %jumpToIndex = 1; + } + } + + if( %inspectIndex != -1 ) + { + %isObject = false; + if( EditorInspectorBase::isObjectTypeField( %field ) ) + %isObject = isObject( %fieldValue ); + + %popup.enableItem( %inspectIndex, %isObject ); + %popup.enableItem( %jumpToIndex, %isObject ); + } + + if( %openFileIndex != -1 || %openFolderIndex != -1 ) + { + %fullPath = EditorInspectorBase::getFullFilePath( %field ); + %popup.filePath = %fullPath; + %popup.folderPath = filePath( %fullPath ); + + if( %openFileIndex != -1 ) + %popup.enableItem( 0, isFile( %fullPath ) ); + + if( %openFolderIndex != -1 ) + %popup.enableItem( 1, isDirectory( %popup.folderPath ) ); + } + + %popup.inspectorField = %field; + %popup.showPopup( Canvas ); +} + +//--------------------------------------------------------------------------------------------- + +function EditorInspectorBase::isObjectTypeField( %field ) +{ + // Inspector field types that refer to objects. + + switch$( %field.getClassName() ) + { + case "GuiInspectorDatablockField" or + "GuiInspectorTypeSFXDescriptionName" or + "GuiInspectorTypeSFXEnvironmentName" or + "GuiInspectorTypeSFXTrackName" or + "GuiInspectorTypeSFXAmbienceName" or + "GuiInspectorTypeSFXSourceName" or + "GuiInspectorTypeGuiProfile": + return true; + } + + // Other console types that refer to objects. + + switch$( %field.getInspectedFieldType() ) + { + case "TypeSimObject" or + "TypeSimObjectName" or + "TypeMaterialName" or + "TypeCubemapName" or + "TypeGuiProfile": + return true; + } + + return false; +} + +//--------------------------------------------------------------------------------------------- + +function EditorInspectorBase::isFileTypeField( %field ) +{ + return %field.isMemberOfClass( "GuiInspectorTypeFileName" ); +} + +//--------------------------------------------------------------------------------------------- + +function EditorInspectorBase::getFullFilePath( %field ) +{ + %fileName = %field.getData(); + %inspector = %field.getInspector(); + %object = %inspector.getInspectObject(); + + if( %object.isMemberOfClass( "Material" ) ) + { + // Image filenames in materials are relative to the material's file. + + %objectPath = filePath( makeFullPath( %object.getFilename(), getMainDotCsDir() ) ); + return makeFullPath( %fileName, %objectPath ); + } + else + return makeFullPath( %fileName, getMainDotCsDir() ); +} diff --git a/Templates/BaseGame/game/tools/base/utils/objectNameValidation.ed.cs b/Templates/BaseGame/game/tools/base/utils/objectNameValidation.ed.cs new file mode 100644 index 000000000..ba74d0d36 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/utils/objectNameValidation.ed.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +function Editor::validateObjectName( %name, %mustHaveName ) +{ + if( %mustHaveName && %name $= "" ) + { + MessageBoxOK( "Missing Object Name", "No name given for object. Please enter a valid object name." ); + return false; + } + if( !isValidObjectName( %name ) ) + { + MessageBoxOK( "Invalid Object Name", "'" @ %name @ "' is not a valid object name." NL + "" NL + "Please choose a name that begins with a letter or underscore and is otherwise comprised " @ + "exclusively of letters, digits, and/or underscores." + ); + return false; + } + if( isObject( %name ) ) + { + %filename = %name.getFilename(); + if ( %filename $= "" ) + %filename = "an unknown file"; + + MessageBoxOK( "Invalid Object Name", "Object names must be unique, and there is an " @ + "existing " @ %name.getClassName() @ " object with the name '" @ %name @ "' (defined " @ + "in " @ %filename @ "). Please choose another name." ); + return false; + } + if( isClass( %name ) ) + { + MessageBoxOK( "Invalid Object Name", "'" @ %name @ "' is the name of an existing TorqueScript " @ + "class. Please choose another name." ); + return false; + } + + return true; +} diff --git a/Templates/BaseGame/game/tools/base/utils/swatchButtons.ed.cs b/Templates/BaseGame/game/tools/base/utils/swatchButtons.ed.cs new file mode 100644 index 000000000..b26cffcd8 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/utils/swatchButtons.ed.cs @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Common functionality for GuiSwatchButtonCtrls. +// +// Note that for the mouse-event related functionality, "useMouseEvents" must be set +// to true. + + +//--------------------------------------------------------------------------------------------- + +function GuiSwatchButtonCtrl::onMouseDragged( %this ) +{ + %payload = new GuiSwatchButtonCtrl(); + %payload.assignFieldsFrom( %this ); + %payload.position = "0 0 "; + %payload.dragSourceControl = %this; + + %xOffset = getWord( %payload.extent, 0 ) / 2; + %yOffset = getWord( %payload.extent, 1 ) / 2; + %cursorpos = Canvas.getCursorPos(); + %xPos = getWord( %cursorpos, 0 ) - %xOffset; + %yPos = getWord( %cursorpos, 1 ) - %yOffset; + + // Create the drag control. + + %ctrl = new GuiDragAndDropControl() + { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = %xPos SPC %yPos; + extent = %payload.extent; + MinExtent = "4 4"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + deleteOnMouseUp = true; + class = "GuiDragAndDropControlType_ColorSwatch"; + }; + + %ctrl.add( %payload ); + + // Start drag. + + Canvas.getContent().add( %ctrl ); + %ctrl.startDragging( %xOffset, %yOffset ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiSwatchButtonCtrl::onControlDropped( %this, %payload, %position ) +{ + if( !%payload.parentGroup.isInNamespaceHierarchy( "GuiDragAndDropControlType_ColorSwatch" ) ) + return; + + // If dropped on same button whence we came from, + // do nothing. + + if( %payload.dragSourceControl == %this ) + return; + + // If a swatch button control is dropped onto this control, + // copy it's color. + + if( %payload.isMemberOfClass( "GuiSwatchButtonCtrl" ) ) + { + // If the swatch button is part of a color-type inspector field, + // remember the inspector field so we can later set the color + // through it. + + if( %this.parentGroup.isMemberOfClass( "GuiInspectorTypeColorI" ) ) + %this.parentGroup.apply( ColorFloatToInt( %payload.color ) ); + else if( %this.parentGroup.isMemberOfClass( "GuiInspectorTypeColorF" ) ) + %this.parentGroup.apply( %payload.color ); + else + %this.setColor( %payload.color ); + } +} diff --git a/Templates/BaseGame/game/tools/base/utils/treeViewFilterCtrls.ed.cs b/Templates/BaseGame/game/tools/base/utils/treeViewFilterCtrls.ed.cs new file mode 100644 index 000000000..a3e055bc6 --- /dev/null +++ b/Templates/BaseGame/game/tools/base/utils/treeViewFilterCtrls.ed.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Common functions for filter text and clear button controls on tree views. +// The GuiTextEditCtrl having the filter text must have "treeView" dynamic field +// that has the ID of the associated GuiTreeViewCtrl. +// The button ctrl used to clear the text field must have a "textCtrl" dynamic field +// that has the ID of the associated filter GuiTextEditCtrl. + + +//--------------------------------------------------------------------------------------------- + +function GuiTreeViewFilterText::onWake( %this ) +{ + %filter = %this.treeView.getFilterText(); + if( %filter $= "" ) + %this.setText( "\c2Filter..." ); + else + %this.setText( %filter ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiTreeViewFilterText::onGainFirstResponder( %this ) +{ + %this.selectAllText(); +} + +//--------------------------------------------------------------------------------------------- + +// When Enter is pressed in the filter text control, pass along the text of the control +// as the treeview's filter. +function GuiTreeViewFilterText::onReturn( %this ) +{ + %text = %this.getText(); + if( %text $= "" ) + %this.reset(); + else + %this.treeView.setFilterText( %text ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiTreeViewFilterText::reset( %this ) +{ + %this.setText( "\c2Filter..." ); + %this.treeView.clearFilterText(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiTreeViewFilterClearButton::onClick( %this ) +{ + %this.textCtrl.reset(); +} diff --git a/Templates/BaseGame/game/tools/base/utils/undoActions.ed.cs b/Templates/BaseGame/game/tools/base/utils/undoActions.ed.cs new file mode 100644 index 000000000..3de6c2f1b --- /dev/null +++ b/Templates/BaseGame/game/tools/base/utils/undoActions.ed.cs @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Undo actions that are useful in multiple editors. + + +//============================================================================================= +// Undo reparenting. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function UndoActionReparentObjects::create( %treeView ) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = "UndoActionReparentObjects"; + numObjects = 0; + treeView = %treeView; + }; + popInstantGroup(); + + return %action; +} + +//--------------------------------------------------------------------------------------------- + +function UndoActionReparentObjects::add( %this, %object, %oldParent, %newParent ) +{ + %index = %this.numObjects; + + %this.objects[ %index ] = %object; + %this.oldParents[ %index ] = %oldParent; + %this.newParents[ %index ] = %newParent; + + %this.numObjects = %this.numObjects + 1; +} + +//--------------------------------------------------------------------------------------------- + +function UndoActionReparentObjects::undo( %this ) +{ + %numObjects = %this.numObjects; + for( %i = 0; %i < %numObjects; %i ++ ) + { + %obj = %this.objects[ %i ]; + %group = %this.oldParents[ %i ]; + + if( isObject( %obj ) && isObject( %group ) ) + %obj.parentGroup = %group; + } + + if( isObject( %this.treeView ) ) + %this.treeView.update(); +} + +//--------------------------------------------------------------------------------------------- + +function UndoActionReparentObjects::redo( %this ) +{ + %numObjects = %this.numObjects; + for( %i = 0; %i < %numObjects; %i ++ ) + { + %obj = %this.objects[ %i ]; + %group = %this.newParents[ %i ]; + + if( isObject( %obj ) && isObject( %group ) ) + %obj.parentGroup = %group; + } + + if( isObject( %this.treeView ) ) + %this.treeView.update(); +} diff --git a/Templates/BaseGame/game/tools/classIcons/BasicClouds.png b/Templates/BaseGame/game/tools/classIcons/BasicClouds.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c863f29f04d9d499098f5ef7db51d11d4fe630 GIT binary patch literal 502 zcmV^-yb0fqrUjUdJAfmi^wjOAs05N_B zxlG?!2gKYD_Abb@7tdZ|we8!x|Nnpg!0r-|Yk_+AgJa>#=dTzx{rJehaR2HnhWGaw z7~b4wU~n-FU?|S0VF+-F2CEMVi~uQfU{;dS5ZJPAr-g)+7+4}BrjX&?+Yb!$rY~mL zxM>}Ozi%)D12C=G+c`1FN-8qAx_B_~bBZyXKY4}W(BXZMSO7YuKvYDmfBMW>495?j zWO(@KJ_8pQH-m|>IRj7}?y9xxS1`PO{fa?URE%NUj?E0;zI|Q50+n0v`pv8Ox|$jT zPj_F2gNODp$jd7-XlVcgON0TWkYW4I%?xYSt@r><3m|X-7?m54(*YVen*Dg;U_ s0kCud1P6e)5UA+_N=h91nF%1k0KWX-npb5q-~a#s07*qoM6N<$g5BcV0ssI2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Camera.png b/Templates/BaseGame/game/tools/classIcons/Camera.png new file mode 100644 index 0000000000000000000000000000000000000000..0722c8515cba616d2800254124c78f8f12d20ad6 GIT binary patch literal 526 zcmV+p0`dKcP)Y#T7XtACbORE2c)1z$ zO>`I-W`V`_!z2ZT1sP=Jr5S$x{LS#>;S+@~U%u7@h3nD1fD#B|5@HOmUc6$kvNLB; zR#SwFgA6%y;vB=>+xHo~ynPuW0+SiyQUVy5K#~h)ZD3flVFN=!X&zX!zL5@tvbrLJ zu!taole;a0je|J@4<9$!P#zv`hHYCnGyMAh748KF7B&V3W+3a;t5;w_Sp^veMj-9x z>%_pu#tIe#IrHd&lMG6#iVT;|UtxIk_&!)1>;gt0$q3ZJ2z4$uFib!IrWwp;XG4uX zCI*B7|Nj9o&jezhcnV(P0o06vG}1a3`~|fnxz2 zjX+m`Tm^~&Y$?$Ji+%-Yd@G=dgA!3AiUIhNDVpIAfXVy;^)nNt1^@&Y03tJwZ~0q5 QdjJ3c07*qoM6N<$f>UDCtpET3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/CameraBookmark.png b/Templates/BaseGame/game/tools/classIcons/CameraBookmark.png new file mode 100644 index 0000000000000000000000000000000000000000..0722c8515cba616d2800254124c78f8f12d20ad6 GIT binary patch literal 526 zcmV+p0`dKcP)Y#T7XtACbORE2c)1z$ zO>`I-W`V`_!z2ZT1sP=Jr5S$x{LS#>;S+@~U%u7@h3nD1fD#B|5@HOmUc6$kvNLB; zR#SwFgA6%y;vB=>+xHo~ynPuW0+SiyQUVy5K#~h)ZD3flVFN=!X&zX!zL5@tvbrLJ zu!taole;a0je|J@4<9$!P#zv`hHYCnGyMAh748KF7B&V3W+3a;t5;w_Sp^veMj-9x z>%_pu#tIe#IrHd&lMG6#iVT;|UtxIk_&!)1>;gt0$q3ZJ2z4$uFib!IrWwp;XG4uX zCI*B7|Nj9o&jezhcnV(P0o06vG}1a3`~|fnxz2 zjX+m`Tm^~&Y$?$Ji+%-Yd@G=dgA!3AiUIhNDVpIAfXVy;^)nNt1^@&Y03tJwZ~0q5 QdjJ3c07*qoM6N<$f>UDCtpET3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/CloudLayer.png b/Templates/BaseGame/game/tools/classIcons/CloudLayer.png new file mode 100644 index 0000000000000000000000000000000000000000..0cde23bc03b8deb3b642c06a0d2157144613e43a GIT binary patch literal 652 zcmV;70(1R|P)q5X(LHc?WHk7 zhR{DUo+(rmAaaCK+9AbGL~)dG=XMHK(L$+2C|XS3MIzHH$vuRH_O{caxL6YJr#_06 z!Siwbte|~-2x`j6Y&U)Kl)l>MQxMamSW{mo*Oq?KW!XDDt31-hhj&I(Lm;HO%B}nS z%IM=vAMTRfH@OKYeg&HvG_{qnwXw3nE#p`q=^vWx(>GPl|(0)=<_d+Sv*N193*ms5_e$8MGnNRdpBiWPW=7} zEPevK0bp=&U|EbVmiL^GCwSOKEgX|25DL^|^>Xq89`y|%yj8=jVdBSk3m1E?Vlro7 zhU_*rYB+L8cg~2|j|w%RjZMP~-wMav!lQcJ3Z6fEfj>*Y{(UXjOr!&pxn^!xb|j0r z`B~;AjJc`L|6U@MafUCF+x05<2)N7qXL#`)Vi3r{7vFGe0s$D!_&=Rt zKh)_c;OGkmhP}@iQ8Zxz512p%HW_OPRGaV_@Q{*dWP(Nwxd7~f54dCn*a#Z%kcn|7 z!vUNIuoLG623!G21h@b*pb?)}uz(FftXO~g{|0zo0Hy~}9+;1uCKlahWO(oy)kYvL z1Zq5hGcy4V3V<3Uu=5Ec!^u}*;SW%a3vh)2B74q+I1l7Z4raLb6tQUr0e}Dl0G0b% Ub_lorsQ>@~07*qoM6N<$f;jJreE}1{rUgjo>{Yjwd3%iLke?3SBD8+Il!@zaRvLL zUk4hQD?+R-EDR>SQc+_QV&vB2T$87K+*89$Kse@w*#myPtahP0ttu?qF6S8~7&!kP q=XG*+He}W04r`j|;itjKBEirdnKrk^h%FOnBZH@_pUXO@geCyy(L1RC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/DecalRoad.png b/Templates/BaseGame/game/tools/classIcons/DecalRoad.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe8aab12026074f82fb213774c5916da58b5c05 GIT binary patch literal 514 zcmV+d0{#7oP))_H|KE?34?b}*-~cSB8ad^enOTmrv9n8p_&~$||M~N$iUALR8=xc1C;+53@bGX; z{{Q=%;men=4}g5fD{uclAj$wvZl2RDEG#N8K07Dp#IsMn;nfTROfbgxuU}LwEsYo) z>@65RfBeAk^ZWM&3`77%SRh&|38I81(C>FnKl-dnvwZ3ZS5W(E~CC5GTIKL&Po_5`5#d7vQ<1ib)?LRL2R`;0(xQFQ0lJ%;UD_b~kY@$&+Zn+S~B3)s8>_Qt1=pBO%W z{>lJMiEyl@smhR%lf)n=FJk~yvmaH2k#ORGP#*>fNpVp0)-xe#o*x+I z%V1<`fLAjNJG$C}v?hSPV4*1R4_EZz1AqVi{QwYP07{XWGKRKTrvLx|07*qoM6N<$ Ef^$XUc>n+a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Forest.png b/Templates/BaseGame/game/tools/classIcons/Forest.png new file mode 100644 index 0000000000000000000000000000000000000000..014caf957604228a3d283412cd358b09840294d0 GIT binary patch literal 800 zcmV+*1K<3KP)9FqRZSj?|t{4bH4kX?~2ko%p8f9h2UfexO&WX0_*a{g{5ifC-b)L z$KL#Ml#~e=0 z{r=NyI9j=-*0O924g0I4)RGk7UmB^D$gLw6( zrP(16*fWzrSL*zg61+Z|6IP_>-n!Xx&ZFx(OfKr0jgGfZavr^Cs?g(6hTm{8+~922 z-A?K?WQI*}lFN%YJtNZ}=<{e&gC+$uO@pDcu#KlqXRq2Z$1+hLJB_6LPG}LZ;9Q$o zfahIpP+&>0U{LZ*Qj_@D_gVAIUE}QIQ7=Xa1acIW3L1v-3uYpM;{oSeIFE>yWQycN zU~~-gz3H$8HbhU1W~X?3O9-bK6X9_J?I|EL4A4eZJZFNggv~Li1Y-m~ZtRsh2EV-T zDfGBfzS_3M3?iV(;ZRMCP`SN$!Rq#*kpUJD6%K(l$ps6`Xd{~ivt2HC)k5#`5!*Jw z%$^0mhk9*czccy0ebcT_uYGsA$i187_hL0r=M6E?!1r>}RhR6kJ7~}^Dqp7TBGatpi^|-w+&m3;d zGA*;4m{}A{l+l)y{#M{eYlMUZ4{7<5n);2*O5WzipJdBrJ(}3c-KrnxXc6PVsZMkp zm~Qey{<~>8yH7{IZ$y!h e-c&XI2rvK#6w_7Twchvu0000BB{wTXWf zIZO-?75qpNNF6{^0C1l{a_?$JwiX#f0yHG5NHRl%q?vHS@8Ad_eEjy}+qM9%Yna?7 zTY&-E&I0XVAzvskS8iSi{QNyHEabkpiY2+fR{gtcSp}r&Y`3tmVcm}Ek6e~{HXDa` zA5u^%7tg76O$X@MvT@tJJ7a12e*vCS=?Kpq5$-;@+V|#t(nwYinnFNR8&DBl?4~Jz zbh{-=f7Egi>a6#6OV7t(I-O)x1>F#u+Jx!d>pq{;Igg);{G&;6lzNyH19;*?JGURd z^*S9BRCUQwcUP5$v;NN5JD_JXEyfaJ0SL??TzUl}BA9r2pDR~NhMSJXih{&e1r33{ zyAMIRoENZVuK9-~T_7~*3Co|d(^X88m6{gGXDShl(WU+yEx7mlpjIvEL<1)W!bu9r z#)Q3?nTY=RTpIcHu?RG4yJ`@FEVy9W*3na41WIA(KN$F=66 z*(kc|J$gny>$}KgzrWI_6#xZr=%ltlzYln05&=fKTu%A4=aR%a*G~Z<%)vc}`-rBb zzRjmDD<*eB;ClNmpnwO1KNW+Zd2Am@VgLiz(Frajj%q5%csHU*4h^0Pw};87pjm~P sJdButHI~r=I%ih+rV9qw68I;;08R{lwOeqP=Kufz07*qoM6N<$g7FYQD*ylh literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/ForestBrushElement.png b/Templates/BaseGame/game/tools/classIcons/ForestBrushElement.png new file mode 100644 index 0000000000000000000000000000000000000000..0fc969d30b12f10e220f4315aade7cffe00a44b3 GIT binary patch literal 409 zcmV;K0cQS*P)Ao$GFi-pn!Dx@`;Zhb3*ujcC6mZ$0F|xo5|oi*WEpUEkB#x9LnLhj zI{_!X3h9Y8beN3$r@29?iN(hWg}vz)!u19sCi=9JNIV!#H9^Ev-THY)H2p?1>qmlS zCB8PjF&gV0w64)*88ti-Q+${+SNT2Ab9=WJm>7O(3l!h83SG=0s00000NkvXXu0mjf DS7WG% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GameTSCtrl.png b/Templates/BaseGame/game/tools/classIcons/GameTSCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..f1801ffe0c1cf0972c2a49c1527625291312b495 GIT binary patch literal 558 zcmV+}0@3}6P)4Tk z6B7f|&5g`&Z-8Xq-ULd)G%x|te+c>H+vR0}S5bglS(b z^Y71a8^DMOL>_vyOo2mj(}(TM2GLw6dbDhUl~uKwPtRcX>1XEPh57+#FvQLG-e3Cq z<;4{5nK@h2kKOLfh+WJsD9ylN`uE4D$JaN46$!v>0J~?;^(FFLN+p|$p1yv*m+jvN z7G{V?|NQt2G@eD)1j!GK3|F2U`|$Dk$1k51_>~q#EVEYjXxUKp{@UKxXE(e&vz}Ge zkxdR&!^&gR;!G3zgC~@`R!Hzk0Ld?Z7#8g8d-37{yL;o$o%Kw=kUV|l#`eFz{y3{U zBgavtXXVCEZ@(*=vdfw9dN#0d@<25J19D5NAV!b;tWhA1mg$6g<*GR-)COli%pWm{B>1& zrV2!W%e(WfGqwouuPYOiD}5(sg&! zBrJg+BLH!xlZ<_Fu3`*;kadIW<9q;UXKW@5Rm}C^0Ue&<&bGFMy^k_>eTD)L2xk>=!9ykO@n&FS@~&b#wGiGw^8R zt2ywM(PUJ#^D|HwK(fBl{B0t^71?N$6w!}1-PvDn&+N&t;-S5e!im%*1;Gn(%#T+rcem}8 cWnhtD(BSN^o|3$}7N~>4)78&qol`;+0F^o=vH$=8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiAutoScrollCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiAutoScrollCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..b8219f5750590b3bf998d30c9f408f6211526975 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X=6Sj}hE&`-5**0as=&j1Nc6$B&(3+LRln(6OnG$5xKe(- zr|PV=vaHRk*!q`>1Z>zOWx&d3xnsUIdtv)#DX!THj~*_(X7z>t*gb1mg;se%!AUoZ zXP0+Ksm~5&JJ)pgGcYqKty&PM_;x}C(4`EXu6{1-oD!M<&}Lz$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiBitmapBorderCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiBitmapBorderCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..b6d8e52ac33b6d33d2919604df57e37c173835f1 GIT binary patch literal 480 zcmV<60U!Q}P)SnsamPi=`>AK0u|H^ zBo5rJ*Rm}0JWs#}MI~2NdEq1l5yPd#xi}9i;zY(6_RzL1#Utz< zq(aX0!XU>wm!@gLR=h{41f?coQDtH;&4I|1r=&O@*JTmMA&I#IFPiZ(gCJGcb(Uu) zc;KistSBlmRtn0YHh4g$>_I$41<8YH_2+19t$j<=IE}y;e23yV1QyZJ0G8|hwueE5 z5lSj=I>JmO=eD-tYGyIvfr^0t^70 WgE;Y?ynem_0000) zDwf`n`s3MI)T&h|4hNb|9Zsi{6HBEM>~;luqk(%pJ@^%m^Ddpe&NZQ0sjjH13a`%# zlbz7->c`UaB`!upa*8QOHNp^kI%}CMm&@UzZ~?O=~dMFyJ1*%Gw%2)6=w6+$p9U)d)ja znTZjXX0Xw?ZmnKt0t0T=p2GKly)}1=DMvNJ5SBQ^6{iAIHoc+0^!a_by!?x+;uV{2 z1?hB}FV9V9=jZ3x+e`9icHej7_|GvDsJ`Vldh_b_&cx(I(BttUeRKrVGVfd@Nt7@; zHimruj4fX(@$vJg5T6ozlI3V5T0A{f@n|Z5zP>&fO@j?q<4!Tx6oq54# m+Ym(YLo~MazBB(%fB^tCzL)}N?m3PC0000E&> zZN#am2bi6k#h}lZ)OCHaudh#TH8?#z9p*SL5&a>#(&;n;fdG6%KG^JbQ(V|! z-`~d&!$S)T*M?z;BO@cS*&uarkl2my3d{xOpU%T-wc@_0)oR%JzJq~*0SKay^iNNV zmZ&5xXERyh;^G3qnHey9__VnRRaHAqcx}z(Pu_l}hOE??+iJqf*uIc6Alq-QC#Q+JYoW zSXp_4VzG!nCnqMyVRs;3%7e80%}oI|n-v-pQjCv{nNPAz-)z+DX3mCe)+nd2+if5% zIWUK&mdhPOt5&P18^*mgo@q#HI&iz)taF9IjgF}|9o$_Xhr@xZzgMP%ZI`Pj)M%iC z_}@Vt<#IdjbUIO~sN`VVQYaL|N>)LStA`1+)|I7FDI}6fD2jrQ8yhX0IK(9lY0X7? z{qj}9KkXMh9xslMj-VU*y^F+iJSHY5*&Zlt`BKTX5AQ__71%W{My2TW*_n)oj{@lJ z?M1CxW5ZL;LpfnoBMxy%)AlpHc=mkQ(DikX*CRX*1~EP{(f0QA@iBhw?IEY+k{r)3 ze*XGJZfnqtC$n?Q?CO^4jYjA;Zt3dUws2fH8jroZjsFv10Le#*5&EzYj{pDw07*qo IM6N<$f($rXF8}}l literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiBitmapCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiBitmapCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..6f511189f0be61b63c25c476132f6e3042b3739f GIT binary patch literal 520 zcmV+j0{8uiP)45Ps5dWm*(A=`R>}@*JsxQxy+n=?1EB2#-AUb@2;r^GFZ?wEWfe%%G*muhP7-* zHv$#CIJ%gbgYVP*Gk?E-64Y`6srZisF1@@Ez9C@Ng=x7v(?8xld1+$cpYNZ5;;+uE zzd9=(q6WoDOD-((RSb;MjQ#if|EwL=%Asw{9K1krQR4u(e-IijKD%)K!FfeK<&w=s zx1QeK#q#&T*H>^^pp)1IrTLUh5gIyob?Aub)oiL(OZyU=liEXsTW5V zfqf6uV8#fExb2s>Puf3ub^59oA77}5sQ^X3{QQ!%Aj#n4>i|J{Zb=QGPN1hnjRT>c z_L~^I@$BaRsNn2_b7oCeH?FJ)vVVX7bZ2!H$aEmyaCVDZhws~W?|%OLiKeG(Yk%_W zOk@#|AEs@e6{PL|<;&MEU%&qP^$R)NG8~hiynMR&&~ijfZ#c2}`={?FQpVrDeZ$nl z%gNguRyTF?%%^XkK|~l$a&13-`%Ey+nW>vBE}ae1Kx*;^2rvNVLK=_h8MF!j0000< KMNUMnLSTabb^)OP literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiBorderButtonCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiBorderButtonCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..25044777f2f8c9df1d4ef0beace57b61a86641dd GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xj67W&Ln>}1#cW78SkK1Az}?Z&-=D7H{`pWV_kUx<01h`^ p3Dblt3`_2CoC8gZ{^MSeuN`c&0fxNX& zJQ_L~-Nvt+%5<~UfBK5ouHQUEaQS1#5X+?tQVLcZr~0p4venFglJ8F6V3)urF5heC z$4=FH{fjNLNAp@?j{=87xjN(iRJWDh2W3<2KCEwve8&)#&7ru5<+90M`>8zrk9T++ zcoY9YG(+e>a6=^fES3)<3D+8rP5Q-=A#rP+vqW3m2Lqmjvee$2AA@YgTe~DWM4ft@~{a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiCheckBoxCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiCheckBoxCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..8e608ba2d7698f8795e394cc413102def5be8552 GIT binary patch literal 459 zcmV;+0W|)JP)T6uR~ILXqvKl;p|~#~Bap|>K)md9Iy3HI0=ll>o2D832aLz~ zERI>`N4TVE8r;d__x4x`i5?|i@{)k+3X$tejlw? z3)N~BiXuZ0Nb*&pn`|}MLQfSm^)&002ovPDHLkV1lrZ B%3lBg literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiColorPickerCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiColorPickerCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..cd9c6ca32ea1e74ade912b791145159e92ee0dd6 GIT binary patch literal 396 zcmV;70dxL|P)@CojrxSunmn_QAUGLCzz{)Fl$*dhPP0-FM`7Wt@oj{}D3G`A0&U+;d3dC!Iv27@Vl-`CV#SebL2gh@B qDRL?5wlvDv1t0hO-+%9?00RI#XOq=|BqQ$t0000}1rJS%o?ZB0gq#(G#Va0#p9uBQk9Siq9-o;XG iU2JZ%mWMSkFf&*Ru+A*Eym1+*kHOQ`&t;ucLK6UF1Sbsu literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiControl.png b/Templates/BaseGame/game/tools/classIcons/GuiControl.png new file mode 100644 index 0000000000000000000000000000000000000000..63393413878ba594a665e654beeca2a30300807f GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;}1m6(0}f4+hFjqT=%g7a-SHZ*Y9*81{vdW)|= zeDfFEtO(QiJq5B!2k!5${}{QqpIjvlu*G{an^LB{Ts5cuQM$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiControlArrayControl.png b/Templates/BaseGame/game/tools/classIcons/GuiControlArrayControl.png new file mode 100644 index 0000000000000000000000000000000000000000..f7340fae469b7e85181d65b29d218fc0363fec3c GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XQaoK8Ln>}1^>7vbK7ZuMi4X7Z*S9TQU2bH=;8?Q4IxtD$ zz^C8m<=NcaEaZ7QM46>Oe1C7>_W%F?#U4W1Y)m~0j$RDH%&iTDCIXov9ZML*Vj7*d oFz#S#&(Pzopr0ABe*Y5)KL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiCrossHairHud.png b/Templates/BaseGame/game/tools/classIcons/GuiCrossHairHud.png new file mode 100644 index 0000000000000000000000000000000000000000..65b06404aa0cffdd3607093c2cd0839b792b9f0f GIT binary patch literal 439 zcmV;o0Z9IdP)O zV6$}~Yb}vd>S4Jg6xhF^T&)lz)D6SX_x+~_K5#tDiZBEuV@%g|_yB>C0HbYN#0XF- zNmEPeSApq!v<8i})`JJfam*ZffQJmI$^$oucZ&r$N(NzKlAv`O8bg~TnK>lABqC8F zje)mW0YOl8aeTp?scD$=U2wd-_9VJJe;{w6oGkK(Vvb%=apUrisl3G`a-|I{vc6q$ zdi8s88186NAX^G>Exy4S(6cO)jQ9~XO%tlS5vO8q{L3K9BM98X)pZ>fN=D#_-Y^Xj zswDZeUc063*gm{~ltYPO{;`8_L_OwC9lScf%hj}t^-O1LRaNe1B+`?=RSekfcKiMF h=!;AK`=!4G7yt#$Gh19hnz{f0002ovPDHLkV1o7$xqSct literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiDecoyCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiDecoyCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..a092c1ee24b7fe77b96ca082a7957b87911af94f GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XLOfj@Ln>}1{orS>yIL-hE*N!l(X=lI%K)PN&_`k9lVaUclkO&UI2*eDe049b7Wgr9w3)(k8g^>iY zISJ-VKaI4F3-|oT3z(37`JLhOl=itt_MH0n@883R4{j&wzyQ#G VI0wT5p;Q0>002ovPDHLkV1hJT&=CLt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiDynamicCtrlArrayControl.png b/Templates/BaseGame/game/tools/classIcons/GuiDynamicCtrlArrayControl.png new file mode 100644 index 0000000000000000000000000000000000000000..a356634e84f1fdb8a72d4bcd674d6985f6b26bd8 GIT binary patch literal 248 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XW_h|ehE&{|GT|XFlOj)Rbdj~7*agXVF-^V+oOcg$Hq3O% z;{CX&!oB}}c>L#Cd%|sWjkR2t-dS(XU=cs9FJ;-E6CE7#zdqFl%$wP9EjQligp^x= z0mnD*^%d{eZY_FQueLE&+EZt45SQYWn-a>?uSIQMzpIkX%B^Q3pMsTCnQ~#C-~NwF u-9GQ%pP$t(e>_g*`JJ>?5Bc;N*%%fZD3o0P&shU>C4;A{pUXO@geCxym0eo^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiFadeinBitmapCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiFadeinBitmapCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..60463c21984988dd8fa475347a6403eb212bb40c GIT binary patch literal 596 zcmV-a0;~OrP)KRo*W-Ez;;_QWSf$iB&!YOK{cSd8ea>n}n^7z%86WIdytsZQxX|s#tBN@)VP7WPT zl|#y{Z$8uKme@#)PfstfY_?u+uiiUw82AG_v}1{rUgjo>{Yjwd3%iLke?3SBD8+Il!@zaRvLL zUk4hQD?+R-EDR>SQc+_QV&vB2T$87K+*89$Kse@w*#myPtahP0ttu?qF6S8~7&!kP q=XG*+He}W04r`j|;itjKBEirdnKrk^h%FOnBZH@_pUXO@geCyy(L1RC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiFilterCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiFilterCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ddc8a7846985acd8c8520abb3a14e7ba2eb48c GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XT0LDHLn>}fnH0!(K!B%h`OUzXgOh_TmLJizv2>~y-yD%6 z96Bpt;{SYm-lub{%O2@yw4 z$jPQCZ@NKvbawRHaxbOt<_WFUX%a=7+4^QzGM~47VetGNuTpuwz|MN+`+x6EXJBR! XT;ijX=bZ8z=pY79S3j3^P6}1#cW7mWY%n8?NC1C^#A|=^CwPp#A^neJ#{tP zLF!)pACFnQ_5c2`GNq-Z{qg$$_c#014dw6SF6DIe_p1keJ=DrQQ^JE~u@qZ7o0!KR e!S##^2N<0H%03fW*0dgI34^DrpUXO@geCy!s6yHR literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiFrameSetCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiFrameSetCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..63393413878ba594a665e654beeca2a30300807f GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx;}1m6(0}f4+hFjqT=%g7a-SHZ*Y9*81{vdW)|= zeDfFEtO(QiJq5B!2k!5${}{QqpIjvlu*G{an^LB{Ts5cuQM$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiGradientSwatchCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiGradientSwatchCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7d7c58bfa567a9afb4f3c214ae04d96e27bd94 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xf;?RuLn>}1{rUgjo>{eFViucD%#H~vlOH{PEa_;%z3G9_ z)no~-%C9blNB&%2b?BxKk4RHu0t5F#hTk X45_;Qb^m?>O=s|Q^>bP0l+XkKsMkES literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiGraphCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiGraphCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..18dfdd8d546c1d15b625e3d012af20b59289cdef GIT binary patch literal 466 zcmV;@0WJQCP)NoPXlP z3H)3p1`+`aK^0&Auxx!My8W5h?a$o3ye9}~*!k14{;&~{;Vtz#U;V+)n_wG&E<;uX z;XeH*y7IfnyZ?N)tf%eR&T+o~`SU%@NzMwwUnNwLeYx_H&1ZfbvxQ+ked5Hg}e+xVGN~l`wm$Ajew4xVsh0>>J`3VT`lVs@^q^9;#_&1m65!=-(LOs79$LR zK;_jH1~DCw4byl_9kurB{JnGXp^x~F=N3o+k`C-OTop18t zueyoQt-}`AHn4wu@t1>_c zesLQv@=o3Eox1HSeJ_af&|~_llqqjeF0RA}1dOC6Z-4*;0Ln_JsUL$&t^fc407*qo IM6N<$f=lk?tN;K2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiHealthBarHud.png b/Templates/BaseGame/game/tools/classIcons/GuiHealthBarHud.png new file mode 100644 index 0000000000000000000000000000000000000000..f661a431a13f0a903039f19a965850e7e878bfc0 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XtUX;ELn>}1{rUgjo>{1%tjz7%ySuxmGm367NNCvm`)?|HsG96m^Cn#Q|ZE=g5Y;^$cj5DQM8 zzj^4u{0q6C1Y8+J#@GO9nv&xw17FiYUu6Q3)FMI0gV$IVX@L z>C)bK9N38IOV@QG!DI)vZIfXbA`lw(tqvv=-~(BfB@#fxo;^ZYmgKsw(2zKeX}8-^ z5Co*_`t%5_m(}!|>+cSF>xOs;UTFxow(9T#E9$;QMB?fLL(ARrn+LJRXVTU)T9ZfB^tyz>9px SnF8Jb0000zy<8>?L9m^7#SI# zJ$nY#fFDesJ{`h{jEsb8z~iL-`}f<}SiO4rii3l5#flY#o%H3)7efPs4<9~0c=+hU zhYvszCSpPo=m9vJa44Yx!l48NpFVv;(SQf=@bH|!aN*mxAK$-!7Z4D5{`@%sCpkJg z9y)Xg;)jTci2C|^!cNM_$l&7QA|c{{HUN``f`S4fY2XhfTU%Qo>FMbSv4N4)^xl@Ln>}1{rUgjo>{Y@v(d$26PuWVUBLqe$5K8%J^?o6 zvwt1dOn$&89pcx-a;3q7Q_ekt@$m!i8U{u-hOo8LqW*7=>;js^;OXk;vd$@?2>`-{ BF17#w literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiMLTextEditCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiMLTextEditCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..0e1dea166ddfec490e9a6a6d579c83f88ae9cc45 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X!aZFaLn>}1&9SNcbhKMsvV-~8uU|ZxnwtMR*&L4g@Tf3} zag;tdz-ZGYBO`OcRDt^;`<6nXP1}1-PvDn&#c+N+R@v~(-~6x?M|hb1$QJ<5=)q) zSPGxi0j(2FnL;z`7-l_s@Sq`JYD@<);T3K0RS@^H&y@u literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiObjectView.png b/Templates/BaseGame/game/tools/classIcons/GuiObjectView.png new file mode 100644 index 0000000000000000000000000000000000000000..e6b832c9a5addda706a973d95fd278daa7badb20 GIT binary patch literal 407 zcmV;I0cie-P))6Kew^*vaWt#`v#>p2sA#?MNM^o%iu1%q zrqA#H3QPUt75>L3%=r5|Pz{8HX<&z%C&J3Wpl1d|AI~3Qets8O9S~?s2;E{}V1jTo zxlo<*_cvxp@Nuv)z%}qP6B`du4Q9Nke&yyzQ)j6lfueyAH9r1v^AWJYMF8P-Ob}+E z((iAL5^+G)8?+gwJY`t;65S=m_WE%5AT)qn#mrD8$KWE!(DsPoC}1#cW7mWY%n8?U*Yg`~TnX<7bYv#J}AC|3ANs z1;eEezt77H{J(FXl#`I8AlQ>7u}I3nvw7mgkN^MwcM#eiv_NVF8wa}1{rU0n@!M!~$qr_&E-#(+ar-V@xS|wv`0?@n zh1ipFexJ+x_B`lam#D?%cWKFK=%oXd=3T&yR`m@kwFzii*n4l1sa{=SQZbq?W(E z|S2B3I`njxgN@xNA{5WB% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiProgressBitmapCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiProgressBitmapCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..a6be7a2edc3bf29b2703a43f11bda58c49de1981 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XR(iTPhE&{2`t$$4J+o#*XQRu3OFK6Gd$Ly8WtCa@<^;x7 z>ois<1UVmX;S*E%`td!x&>WT{`TzH{e*SO#Cp{%WWyM2YIo=E<1>XFBd(_T)zMnA1 zC8BxPmSbm+nsw$XEU;A_a zb@t4uu{mSolJ(>CPyQz=d~SY^aufdidu^@Ftvyld-=Ck%0tpPt^UTkRO*TycI-0@L L)z4*}Q$iB}Ckku$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiProgressCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiProgressCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..5c07e2345db200fa2e1b7b69680479df5a0c466f GIT binary patch literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XqCH(4Ln>}1{rUgjo;mNs+xzBvYI+Kf{n3-2f{ZjW;h+}@6 fTqTgjz+u4fGg|JZd4t4JpfwDhu6{1-oD!MoNjCT6O8t7!l9`d)kMH~B`4e|NkG(O3VF(a&BRmlid2WBp@pu%iwEzN$)>^E!^5?k~ z5s@TGf4u=Cusq1qog|+rdzXViG)8IHR)>_guMQe@Lnlw$Z)}mC0rfC4GTY$&1 zEa`b3+wGP(j;ZUKx~_@i7-I~c=TVj=z~e2ztSE|hwOZjg4#pTn1QEd)1HkvyilQhg zV0Iqro}2Z0y&I3mUpJf0C#94(8ja}p`zWPo+m^**!F)cifR9<0RsVhE)2aM(J}28( cul)A)7bN|_&4cht&;S4c07*qoM6N<$g0?E>umAu6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiRectHandles.png b/Templates/BaseGame/game/tools/classIcons/GuiRectHandles.png new file mode 100644 index 0000000000000000000000000000000000000000..11e5a06a9e86831cbe5422c166b98fbf2c7cf68e GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X@;qG}Ln>}1?GZY6lz}aXFOvakO{hl+# zNJ%6}H8gZKx*T9C;k4U)&|&9KkqLkHl`~tKO?ao`vvBv;v=e#@udroVaIW@#@j}I| zBI?j%p^6=hmxWm8wH>iZRR8<`|9ll`1|}W`^)y*Yo%jeDpv?@Pu6{1-oD!M<(pE(1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiRolloutCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiRolloutCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..ed922abdbd8ea159127c9990b40af054b7806a03 GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xnmt_{Ln>}1#cW7mWY%n8?NC1C^#A|=^CwPp#A^neJ#{tP zLF!)ppB&IA*)c^bQ@zbX%lP7=v^5svnxWn8d2M+xB{ysiG zKmSkK|KH#FS0%o`w|7b9t-E)7J+4}pzhlarFkynqJvKEqp@S>fl9clv9%?<3&A?&6 Z5Vp%@anyoUp+M&_c)I$ztaD0e0sz&GVeZd!nbcfzJLENARzGk`Evp`I668WKYsGkrAxPO-;RigASxa* zGBUWhxJZb&Lx&CllZJwVg1){!Q5tM*ZGohxrzgY)MpBbEK!5=N_+fLx2C}%~00000 LNkvXXu0mjftV?{> literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiSplitContainer.png b/Templates/BaseGame/game/tools/classIcons/GuiSplitContainer.png new file mode 100644 index 0000000000000000000000000000000000000000..752127aafdaf38a5289f44c56a87d7866fc26854 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X0z6$DLn>}1#cW7mWY%n8?U*Yg`~TnX<7bYrh~M2_ZyzAw z`*Q#P|NJr*43|FqJ})ou|Gs@nW`}}WLXv`DPnN{h2KB9Sb~O^gOsf?H7+542*wUmc UMA+h=1I=deboFyt=akR{0A=$xqyPW_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiStackControl.png b/Templates/BaseGame/game/tools/classIcons/GuiStackControl.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d34ffa36415d4361fa416c49e4378be6e79d93 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XYCT;XLn>}1{rUU*d-Lz__H};_IB*mc6>4f~8sFPr{{H-Z z`$?06SWNd+{{3}(`+A-7cXzH`x%BVLOJPUeH90x`c`@mN!qayY|K7G@hJ+*U`+IvI zKYW_}=i}q!6A!n`+t>L7G*6f?VQ(|@}1{rUgjo>{eFViucD%#H~vlOH{PEa_;%z3G9_ z)no~-%C9blNB&%2b?BxKk4RHu0t5F#hTk X45_;Qb^m?>O=s|Q^>bP0l+XkKsMkES literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTabBookCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTabBookCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..41e09bd658322ffe34cb513787872d6a5da1f2a7 GIT binary patch literal 466 zcmV;@0WJQCP)v&<w9{dEwjF@8Hs0+??OOe#fHW-`{`dPFw&YWI4YuFBdPz zyo6F@K_&?G_uoG_(l^n~E=>h8{6f8eHb4Z?ZTS25FI*_MJRKr(c;B&IoA)70A#C`E zWCdL0^~<-FEk($J*lmD|1V#E{O0mHGfUW^7^7`c)G%2_Z7#jZmo6$RM`BHa7PcnlHm1%VcIY;kSsNced>SsQtg7Jb zY474`&&|V)W(3rRuV22FW!HcG^3BrDEFjEVTvC()2LK2#0Li1(^b literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTabPageCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTabPageCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..ac58cc276acae9e6b0cd3c625ffd617946e04004 GIT binary patch literal 331 zcmV-R0kr;!P)i(r9Rhp1kCpdvecx_uj{j$<*UpvM4jnr6rF_L`}1{rUgjo>{Y@v(d$&o_BTldN<*hZ{D~(+oVwZ z?afW&>ThrMR)4?8%q%W0t`qjhooRRZ`?~M%WG5IFKl2F>4_9D5!OEqy+HoV}We-P3 zN0Wn&5^FT(wN=Z$NLEx_*ww}56L~Cg!ZZdZ9tQv1tQDJnKD`OFo59o7&t;ucLK6T_ C|4Tjq literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTextEditCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTextEditCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..114a30e5f992be5b3ff30a58533415420a6ccc90 GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XW_r3fhE&{2`t$$4J+o#*XQRsjqtsJVy2bT{Rz}6g&sS7c zeQJM^VTHuQC0AF6FP?e*=1tB-10y4)IV=mmy}4;z{q4=(>hJfMnZ?D$=hQW94qF$a zsi9$!eQk|t_O&xJ3?Kjf{XPBsyrU}_FMBLVTk~y#%$kyG3_RKk`Wx%ku34iZ<0Y^@ v`Kv}zRWjqTvaBqvkDkpFpRE*CIKXgMCS`i<&)w62j%4t3^>bP0l+XkKdQD?V literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTextEditSliderCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTextEditSliderCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..27844cf39a7797622d7dc9d48f2990146bc01300 GIT binary patch literal 289 zcmV++0p9+JP)7?*lz<+9xChw}2%nG| n4~#%eQbaS-Jn@mN0U*EtpQ@dc;rzb100000NkvXXu0mjfzeso{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTextListCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTextListCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..5369870c027f94652fc2ee400a62ff5a146dd72c GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XtUX;ELn>}1`FMIJDzI)~`1t1L=FX70vu2%QN|d!IVAvnT y&d%P#d!nYPp|jD&;MJ0ek5t@(CYw7nFf#Za5bu89EU_AB41=eupUXO@geCxr5-=qI literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiTheoraCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiTheoraCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..2cf9555650d8a26434bbfc274d4a19eaff565a16 GIT binary patch literal 636 zcmV-?0)zdDP)W3FN)+z4_+*o>3G<zC`#<{k*n{x$&p>h1fAMQ3hI`?k$3N(n(ND+-mS zy*S?7Zf8oreEI}EWy9+aA5WybcYDUSg>5lznWW#4=Y`I)Sm~aWUp6*9&YwSeNWd6O z`m=>6ge#cM_|n2cDy74j%eg1`m@V$yy1Caq0GQyG;0-32Ei5W5i&4521TU7CWfXBr zZD!ewjXCzz3YlIaH@0@J z)yDxItp;Tl+=1Ru&^&b-2VyqRNs^3KW4L_q67E26CHNj{%gJe2rvMv W(@I4HSw)cm0000}1{rUgjo>{Yjwd3%iLke?3SBD8+Il!@zaRvLL zUk4hQD?+R-EDR>SQc+_QV&vB2T$87K+*89$Kse@w*#myPtahP0ttu?qF6S8~7&!kP q=XG*+He}W04r`j|;itjKBEirdnKrk^h%FOnBZH@_pUXO@geCyy(L1RC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiWindowCollapseCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiWindowCollapseCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..09e99cdd1341ba476ecc8d8902d220f2eda31f27 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XdOcknLn>}1{rUU*`^RK=i4NvzVP-wmWp71<#ivi0aA0xX z&#tMjUcCyMB(y$mU&_yKf2H~P&rjEn|Bw+78oIOc^Rij1PBpWCmkig7Kjy%d@bk-0 z?}`eEiL1ib#T;cyw5|Tvv>L-D#C!dPlI=YhhiAB h!J*D_A%_M=hBI?@u4igz9{@Ux!PC{xWt~$(69CXATPpwn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/GuiWindowCtrl.png b/Templates/BaseGame/game/tools/classIcons/GuiWindowCtrl.png new file mode 100644 index 0000000000000000000000000000000000000000..09e99cdd1341ba476ecc8d8902d220f2eda31f27 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XdOcknLn>}1{rUU*`^RK=i4NvzVP-wmWp71<#ivi0aA0xX z&#tMjUcCyMB(y$mU&_yKf2H~P&rjEn|Bw+78oIOc^Rij1PBpWCmkig7Kjy%d@bk-0 z?}`eEiL1ib#T;cyw5|Tvv>L-D#C!dPlI=YhhiAB h!J*D_A%_M=hBI?@u4igz9{@Ux!PC{xWt~$(69CXATPpwn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Item.png b/Templates/BaseGame/game/tools/classIcons/Item.png new file mode 100644 index 0000000000000000000000000000000000000000..94c6222dc91a2e4e9a7041a39dde492197a633fc GIT binary patch literal 766 zcmVNgkA!{dJf8$@^$388DR7l#$!|HPAvkiD%b$1YoY)z%j7v;a23(}uS9%HfU!HQH~AGKpC&1> z8mRUI2af|;1>kG+{tXoA5Jkt+Y!0L2AH_r&K(ZhH8gwmxBQ!=E7jX0hC9MLB2_Ulq zm@0i=74UG;OTXWoX<^-ECM5u`2lsleV4~|HaQFz&&;TU9+ca&i1WZ}}FoWB};4>+S z;qpHKreHU&4*7mPe(QPHc2Y{!)e=ZOU}@wzjODMWH|D>NI zw(9L?y);Z|^Aj#RM{5lBIXJOS$Y&2E$cGNkwUFmUX&5qddbgr2>Y z=WagqPPOee1Ce&UIaMGALC8Y40Kdo^l1Dj2AwRgxW$994XN0hs+P3kmS~qFx)KEns&@Pfu!p{DoRYmxjD0*eDkQl5i0=&3h5{5Skg%Ng+0NB1xg8|>8N;)#nmp#mP6gXlmBdn6F9LdVkRBA;;r&gTF wECXyVwy<-D*g!{^rB5}o$X4TQ`yT-Y0DX2dCq&dl%K!iX07*qoM6N<$f<*dNqyPW_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/LevelInfo.png b/Templates/BaseGame/game/tools/classIcons/LevelInfo.png new file mode 100644 index 0000000000000000000000000000000000000000..d7d757686c028acd2b019a9cd66015d1883cb815 GIT binary patch literal 744 zcmVP)e$)b(jE1j ze!o{GsqPY<^S=JRKb}{@pTFdd6n$?I_#Ep>bA$|G9xDr(esihMF+ksYaOsyLQ!|Jj zOJFz>g-k-TTtad6GYTKxz_IE}B$}f@=I;O<=d^G1c=F_>t1!ZT9QLa4x@1U_gnHA) zR=tH>rH0o}AHiJvvO@M3IEW$wM5Z2)bL#xHo0uN)VIu5-5p*LqElC1^z72_oVh+HoD(4rjs`T03~E?jeu@1024<%Wcr_VrRf0@4 ztC4Yheg8J@gcC0m%6>aCJ)4}Fo)DSY`N*IPhjbO^k_OJ6FyK*TxCjb4+YVqkGD@{N z%*~vbQHlq~J%p)v8FH8EcfeKpj)m1~1J4&1v0p7N5fktBnD~sCb&(myB;h@C z8|Fb9<)1C&Ijd>mQ{EKLm2X+5@)b(n`0Fvr^q69&PK!PBs(9KwFz%Vlw*UE<{^L3S a5?}z(x-J`GpTO||0000LQ9u6@NgBql0m9vtTC&5wqwbsDsYU=pyPbs1DBNAEdR?Qb7nJ*bmL|cHP>7rY5mVxI7QDJOiiAd8opsIx_q|MW5`X z{d6hx)Sp+7s4Tz?KN3Jxd3|LqTVH&!J=`}FMZNRG|_Y{}+ z}1{rUgjp4pWlg~85Qj$!J9%cqYxC`z!g860Ao zFma-w^QWaeN(@Km8yF;UyND?{-4^Wl{&+(Ak{hgxUpCBonQ$P6K_G!4jKQetXXBYe QKw}v^UHx3vIVCg!05zaE0RR91 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/MeshRoad.png b/Templates/BaseGame/game/tools/classIcons/MeshRoad.png new file mode 100644 index 0000000000000000000000000000000000000000..8f33e23f4c646ae959f28b9369a65bd473f26dba GIT binary patch literal 430 zcmV;f0a5;mP)49x!=U_*%;Pi=em20B@C)$a43}$HZo-%o z!3F>|g92l|o`JT2cYp`M1o8Oc6A<+P8arSY^ovW13HSwj6V&|Y_a6pkW+sNefB$j8 z#2A4t_|HH(I1hAz5hy8x!(hw714Qfev3Ee>Gck|}fG(%Z0H7Br3Ij$EapmoQ27mwq Y07tu0zf2eS^#A|>07*qoM6N<$f;}CoU;qFB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/MissionArea.png b/Templates/BaseGame/game/tools/classIcons/MissionArea.png new file mode 100644 index 0000000000000000000000000000000000000000..91bb1e219ef12f670097cf041707ce3d909f5b81 GIT binary patch literal 591 zcmV-V0HVR@$ z(uuifB50#nXdUdLsbc;Dv9J(C2*P#O1gvcQRcNkC5wA* z)402N@6GMr9%7@z&c5Aw-}~M-@69NTR!;%6;%?*2<=~I+PUO!u0iYB;imbNZv>pTj zzv%JBUjI{qeL2_Gsza)Drh=u7-?+S{I5z=p>{Ow34X!*T7{)S*Bn>lnJh{M7svjny zp2Cs*EWb!w!mC5&% zFmES%B_<-^JtvbbNVGWUdhX!l=n%Q=dQ_=gb`58zhv<6Yzz~M9N;`1{B$p(!YzCMI z&W&<&0Q)2`w!LTzw)$Gg+B+X%Wb9QGJTf+p_J@z_epR1n6j3#$vL@9(h~oFW{HF#6 zn|#t*#JXUs99Zl7Sk5dUx49B=F3wM5@6Qh2OwHEMgG9x)z>7W~5+B7M`KI7-Y;7!) z?_2#a`4%4%@M5iYg~0Pr`11MIbIU%)T%2DDNt1$=>@RZEuwUN2m0-VI63l9LJ5u0= z&g#)$BH6}F`n~j-Th+J=kc^yp^}|mb5VleC(Q5E1eH`n8l3v@EHEJH#D9p@*E|U=4 d>pK4zU;uA$%tZuoq2>Sp002ovPDHLkV1fig6yX2> literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/NavMesh.png b/Templates/BaseGame/game/tools/classIcons/NavMesh.png new file mode 100644 index 0000000000000000000000000000000000000000..056d3c3ac9e087c6fbdccd35253f63341dd98745 GIT binary patch literal 106 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`hMq2tAr-fh6C_L${{8v+-}7Ju zZ$iWm8RqjlJ_<4SOEny4$m!ZBX_dtw$E@LXikXd}Y0u(3&OL{^fSMURUHx3vIVCg! E0PJ@lTL1t6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/NavPath.png b/Templates/BaseGame/game/tools/classIcons/NavPath.png new file mode 100644 index 0000000000000000000000000000000000000000..35b8372aec4877e23bdce467c6b0ab3917ba997c GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`J3L(+Ln>}1CnyLRWZ(5qxUggW zfi<=H_TAqO|L0l4pmz2*tAek>EXUXT7`unEf$7(8jLu(jZ6XyoYQ+{AE!b-@RQqYM(0->NRiJ`krQ z-msUch?7-shQEc(23Khho)_f-YaaLpD0mheYE)HPkh?)5;o`pg{svYD%^ZxmEN(5x pWO$Ra=n|90;*7iY=N_rUU)J;OXk;vd$@?2>|YSS)c#_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/ParticleEmitter.png b/Templates/BaseGame/game/tools/classIcons/ParticleEmitter.png new file mode 100644 index 0000000000000000000000000000000000000000..e5489fc70e7973f22d679349b713b9920f422ead GIT binary patch literal 773 zcmV+g1N!`lP)VHkeCvz@c!&dxTM z&g0t3X{D$w%SegH24N`HRT0q-fn9XdO&4`j&_x$rM9@v)O+Qo@k~dKl30Xl{EHok% zLL4>I>5g;rtht}FbG{CuvV;VC;N|_k7v6{GecqQ~6<+TsspnMMI6A=&+)Jh6Ljc7Z zFs&~1v}q?Ki+)SBlS~uK+gepN(yyx8zX+J(PcuMDRC-pl=#GG&RQMWgjsDHnqjyBn zv8Do}k?|Puz7s*tCSJzLZkUHZJCpTwFL~bT(ek=)S6UlSs;U}YMPSyC;DM2=2++1z zIero72!aa@)HRKS9#Mu#$0SSKYc^H?8nxU&E|)F#g^@G3PAN)p;2_ZV3Ftfw=<~pp zZs6`I5U(Hz<+cf>zLhp_a_=*G$X_G84MVY-@w_1!ZfDYyZfutff@wgT0xsOgWQ_m* zPU`6M?rn;&iqcS<%0!3Fe;`t4E3Sq(3s#el*HFgomD1^*GL(HP5PJULL{O;9F&@`D#L}3h!@~Wgg2V9ak{m=CF0##6Uio0Hm4-) zH&j?x)@Udoha~gvP|Dsvk+&-aV%1`0wO^I;>JRDoZ!sE{86;*;aC`eWZ{ROf;h^a>gUi;3mO3V9YIf{M;T!&js@t z-IvXhSL-5J#$(A=Wj51u`8@Ib7r~Mm7thm6e*_o+O^6B%Ny$dN00000NkvXXu0mjf DP8MaB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/ParticleEmitterNode.png b/Templates/BaseGame/game/tools/classIcons/ParticleEmitterNode.png new file mode 100644 index 0000000000000000000000000000000000000000..351e4bd729c5f960cb2836069e06d20ffc63319b GIT binary patch literal 426 zcmV;b0agBqP)%%=$Z={USRNNJP#xdz-$F|1_mBM28Oap z3=DsPzUZoAVEFctf#KmBn8gL7#jM4zSd^|+y3_{#E^UE7#P~O z01f(m0jSOqXut<-UO*Vq(ZawW!V5IyEd#^LTMP`sJN_ z_Qyb*fg1k+ZD)T4(hO2kh#ng(SOV67feR>n`WOSl`YS;0KL!Rr2?ho(pk3eo89)OP zuL1H5-~KQ#%sv5PPXLR|yH^00BO)n-Y*_$JJRm0*g4j?o1Dxq#z%moX1^@&Y0O%%t UrD(3ElK=n!07*qoM6N<$g8hA+umAu6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Path.png b/Templates/BaseGame/game/tools/classIcons/Path.png new file mode 100644 index 0000000000000000000000000000000000000000..945102557bd5c6900c0447170f527158204d13a0 GIT binary patch literal 323 zcmV-J0lfZ+P)3z66@GmQOC-(!c-w-B-N#5-=9M$O>@48SsHqNkM?H z!Epo@z!sQ?JM-Du7!wmMjt*G!--%qpUEtN3=IqQOy&LK!BBwz=H2%%2GPB<~yN;5K zoQM?c0W?&ePQ(eLB1U3TkCTddd-F>7Knu0pgJKO5aR9FWXb{IzB0H-R+jNB+-Ndf~ zb(A>6(E<~E?=+d<+0WbcEP)OiU?NzBP&z-4-{=IQ;66^NJy{ZLYN3(S(XeubUIkgW=9FHN+sO)dOTU+ z{q!*zf#qr}ojzG_ER}+3ni8)N2)!+vvOv2J%19mgbiwOE#OF;9u+fBftQbLp~YOwLydc0000^bM*{px!ll*F>HGNj#BwAWESvbnA>Qq}^69PBNM7{Cqn* z->eeHF(`@xSvlA{>Y{0*Hc$ts*AFju-E0okkti|%7Q05p=+x`mU{y`d(xpJ1K2c^zW^-q3!6B@ZTpXW3orn+ Ws_E5fA*oIP0000L5e|ZKrI*yotSW)P*f}0YO8%c-`%yjUV8>_^4xph z_dd__-M#moCL*YXrfEi&87we`2)|WI5F6SNc8$7CV=QX0#5)ok5{a#d6e7f)NOVag zI3TLlq_Hp5$rAL5qz^=jmm;NW=5y2Nb(1DuF*r7j2aOTJ+&s`b0<`%6&oD7S^Z`AC z!0Z&PSF4B9z>mNro6$w|v+8B;=5{A@2QV=XTVIT5hcl$Y%MWvbxy)QP*Qlyu6W#d) zI`ms|vqTE0JX7;Q| zD5E!uPk_4zot&_%0S84lOt!Yo$5&7Wse54mR8bbm$u#!NF-X=hJ<8d%NH#<)i>!~; zhsddE{tC5b#;!)8KLGh6kW8t)WGkYZ0govWc_7E`=f7QPZUz~=QzT#JEWDM>y%wRl t{EZw#M2LV@3bT2u_8zn}_)q)_FaTq_`PI+qmRtY;002ovPDHLkV1i{@+3ElQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Portal.png b/Templates/BaseGame/game/tools/classIcons/Portal.png new file mode 100644 index 0000000000000000000000000000000000000000..0dec3b0a57243ce5a9672921815bde55437df29e GIT binary patch literal 390 zcmV;10eSw3P)PXD ze*5)nJre_kfMNq!unGS8`&Z%H@8ABMtgHf`e*9nn(ht~KSavcqF})z@0-$Emk3W9I zOY-pv2yk*T$O#HE2y$^LeEj}B1}Mja*MOgY{^$a2%mQg*WMpJ`_VFXbi%*{z_&7Ki zqy+@HfpWzlH8>6U@%y)o3{dlj@8207zI)fW^2`~=<)=?G-gx^9#)eZdHaQFfzW@G>!Ux3$2xF7Onvj423D<~Aj!;6v=>=?ZEU;V! k6ZnP40GJ%IdVl}}0Kid_iO2v$(EtDd07*qoM6N<$f+oYJUjP6A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Precipitation.png b/Templates/BaseGame/game/tools/classIcons/Precipitation.png new file mode 100644 index 0000000000000000000000000000000000000000..8e2cc784c247cbd29306795ee4810d2e07dc61ed GIT binary patch literal 381 zcmV-@0fPRCP)IxPd1{ko5@H0FHYGwvn@#h~N&j b01#jRp$uqKZpxl|00000NkvXXu0mjfb5D@a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Prefab.png b/Templates/BaseGame/game/tools/classIcons/Prefab.png new file mode 100644 index 0000000000000000000000000000000000000000..6786bda75362d46d3c2d6566550cb40fa1703e41 GIT binary patch literal 688 zcmV;h0#E&kP)@4JsBNrL?}3Hxjr#uzT!W`)KA=Ou>IS(#N-S+aAg z)V6fFe(hpok%!*(Bnq4)3fl=MMB!KPxXp;XLnKp?8MLf~0|JUf%76&2Ix+4{PoFXk zU4_ab@X!Yd?ZSV>=FSu@7>e|Q2|J`DV&Vjm8Zw96u3(bYA*lvTGmnwHiLt{*r=;_m z;NkV%MzXrLiO=6Q;d%i=o*;?|q@rac2zOpYb1vp&SC_?_(+l`fdHvaK+p_X=)v+Q@ zR?1K`4QpK&8$UfbgBSq`sU%W5m_d;s#y+hPStIzFtXa0Xh$nDiDe%43!A9yBBhF`y4Ly!VR^i7x86jv~g>>*B{LO=()&RNA{32MpElta2vl&e#S{5B|LZ1tn`H0U^CYI2$f z;eZ}TaM`dRUf!&>IENim;uIr~nBsi*X#+74WI7#@RC?37=HUYVb?saqo0Pg@Q;JAv5Ip_msaX%GJUl915ioq933QZttb=D+@9`@PP;1sDKp WIwKds;8xiH0000b@?P)T2<+lOUoCOL+^wy0KS0s3*Zmh52(~nD7o}nPCWT^F+Yce#ccM>>h`k@fucuA98qIdxZu$LJn;TdOS9(H5(r-ksp6E4I zzvyiqtgo;2R=ZuSKIvjP2yl5h-Ef&3Ft$LP{XYMd%$r~mMG-I8$ZOJ37I8x%*&o36B>$>~b@@<{pxmaH8>_G597_)zGl`BmI%5Jtk`SzK>R` zRqQWWLZW2?Vm3>v32seR*f-js?YE0$F>7UaRZfr<1Y*-^B<{3M5Q?&*dZl*Ju_W;5 zMY<@8MfC}fhxMntO+iM|)!J(~3Mb;F&S91$h~gMm(Nv0M^lk1xxl5pG>dPw&t1BY0 zxD(=>_);&vM{wsaJSGO^rxO+(VvgN=%F=<@2WXLi@I z+152Ljb)KZIb9$vg%zYAsP;o8(M==7`l9^;?H5#EgV3lC5r*c6>O%l)yZm4n#8 z$Qpt^ygXT(Nk!j)!dwF*YcasCUwqd46u72BUx9?=6l;tl8bGW z6Og3NPD04dr6+ojW_@#cjsM3YJyq=x-HoC}fK!Y#5JCg;nNNB)fv@@0Hq+xjw$!z^ zGm4y`YB%7r#}vDm?53IFNs^$|pDst*r}K>n9Ou3&lnR<63rH9ea%E_l6L^bCR18c3 z#U-W+-nC%o`R@uLT%TSPLnI}`!^TTF%WQ@~i`yiVATCK}o()zm{Lop?eMd@{b(y^n z8*Y^585o=I5=3W|Sb&OV#H`rtNSoK<2`DHR6QREWpx**Yd8Rdosd;L~<8uNoR-18p zA3CxLG}XZ&7H9t);oy)2EXH^<4#x;;c2P6F>*sOe0FUxQGxz0RPu)rr{1spTU?>zXBGx ze&Y%*=X2l+gNY9x-j8o>s{soD*$&1=<|r`%N(Vry07yT8M&$*V93#GjR9#aD@;4(? zTmhO^K==m`FW7T>!wW`2Nf~AUnpX^f;wSKCM0}YE20#WdQktCs0t^81$aFj``0tkh O000056Lr literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/ScatterSky.png b/Templates/BaseGame/game/tools/classIcons/ScatterSky.png new file mode 100644 index 0000000000000000000000000000000000000000..52257b0bc2c5c1e787dd2ab6a1a6b074113d6a79 GIT binary patch literal 624 zcmV-$0+0QPP)b?qW16%F51f1M_kHL5&hLEZI~oz;IHHwupiX$zZ=5y7`heA?C16T&twivB zHhmIMv~8)i50hk;l_2of`QAfdd<7VM2{Z>>YF`v(C8*y;=n~N0!5h0kRRwT62*sf1 zzW~Z1j8p8g_Z8Uv1pHnGDlkMbFW){qP zaS6!mh}*dXUErX8HX)(sa8?I`p$5!+m_v3u2Zw`jr6r5rliY+^2KlvbsY|rVIZ*1c zZ@c?e6dVly`Fi*-)T5CBatt8y?C}dY;5P3#e+==`lTR!q#5pJ|nc6yZoT>8_N)Z-$ zSy^61Y&hqQZJ#x#lau}SMJwv`@%?8imc1RKpJ}44%`A<*N>D2Kji!ug8hSoV?7vm$ z6h9(xUB0q^r)aQ;&rxzon2Aa z`u7ba=jU*`){Pt8HxX*G127gJvF#T=TU%STxP+Pl171upNFWh6`mVM`QN``@czia< z;?e?MPZ4vC=e5c(9e14n2rvK?(bt=24X`W#0000< KMNUMnLSTX){upWi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/SceneObject.png b/Templates/BaseGame/game/tools/classIcons/SceneObject.png new file mode 100644 index 0000000000000000000000000000000000000000..59079e3d7b7ca0638d6ce036843382f6bf8bbfb4 GIT binary patch literal 369 zcmV-%0gnEOP)J%=P)hh4v57VW45?TN@jz5K^M^_{>CS{{_+}fSa@(l}e@k zdfmqIavfn5q1Ws4Z>uO%jez{KLF>>otJd23N|xHoYZu-39?E5d{W^sNv@R4y;j}Rt z`S|R6=yqLbS_xdjaOfipqw`q=j;g9G38YNmx*lxXM76enAQ;0B0?aZ-UP=;*z-|6u zFD;@{DdYX^18=V%_$J3*5rHzPzHaC@^K%sp1|uRdV45|`E6s?=PNOAHsD_#GN#b?7X zw&-~Cqd{hr-lY(t9}sBZ4XF!S29`p?qk9~S44l=iD!;R#PHEEQv^iRmB=G4x9*5BA z&fmC86a-Niv2Xj2h*P8NJ&bv+*+1xIxOhp!xK9);DCVb!pAgaw|&=hX#J*JG)P|p3;=of-|S?pNDcr1 N002ovPDHLkV1f}@2w4CC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/SimObject.png b/Templates/BaseGame/game/tools/classIcons/SimObject.png new file mode 100644 index 0000000000000000000000000000000000000000..5c143982fb3bf199455b52f352ef9c77b3a24241 GIT binary patch literal 358 zcmV-s0h#`ZP)F=Mv4A}6$zyBih6H6Tkx*#X6_zWdpVEF^IjUog7{>5#8hK?G8t&=4K z8#_D0v&YXE4(~h8@aEN9;=+K7n~TBO!=8bSjSUo{3{oRJAxg#o^F@$<(|26XknK>S0r0jH0jXFykv5s26f0NK2C-A;zvH|`RS z#>KN&f@uy8c7~7`e=wVkosEH&jgu}1{rUgjo>{fw=^d`}7_}IKzB{QNCm4=xK7IG! z>mO-J9(OEvbUi%2d^>l;nbwL}2Bm-h{&gxUCOp$nejbt)>aDV>%C+tc`xc6(AJm7C-`}5m*BR88Bk4As1J+mzLe7U=- iw;9~LHXA!MFfx2lbli6~+`wDnJru|C)u1>nY!R7J(4vLeqSi)>$RG$>1Qk6NtqR>Fg)Jfo-Dxi)DM#B_jEy6q zlc_l}nmRi3b*|(1K%@ibyZ5{2{{R2;_&!Z3g{En&F}vF#YrA-l_zl9H0X8thx-TnW z7n_pnqcz zRF?18L!Q+HF0zYhJe|g^QWL3vz#B?l3`G212E!(Xr;Uuw7?_wdP*Qe457p?4X^9|I zSCr;B=gwqe0NxMHaK~qcjBM2G6kS0J&XffrQH^wK6i04>Kw3Pj33>wlK~KY@PI^Zo zw2nw)x9$Op4Sx9;7v<(Y8(&A0$go8TnGPc(mT9=12?9F0`|0pcfi+c{x&scls7K8aYiG(584?n)>Do}a`C0VGp2O#BWQdVL@ zVwEcd(Rm@51;3o$sR_uEI#0+*QzRRsS=WR?FDl@L@9!2%6FZq8c*FRxhaq45$2{rJ z&OG?pEm_#(Wo=G_^ehotfZkS??#ZQ<45lYVNwzsfGH_8G^~aUX7ICZ0Q?m1Yg5QCV z4wMVlw0a61;#tD(q3@#;nRCKo(xj#L frj4%kKLG{+V2I-QupMI#00000NkvXXu0mjf-GfkH literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/SpawnSphere.png b/Templates/BaseGame/game/tools/classIcons/SpawnSphere.png new file mode 100644 index 0000000000000000000000000000000000000000..df46d9df567e4b2d1a7a00403962c128465a8562 GIT binary patch literal 748 zcmVXVP zY{RzEAkAoKL|#NSNs?|#t!`A1g0A~3w66LOB1Tvmgu<{c3SBp)Qc$R@TD48*)a7*9 zXj4D4bKdDFrK{eL^PQLXJm=v(=RB{3eM{dBAGuyK_U_%|C|QuqzcCh}t+QydG~=*Z4_k39I&XF$^D`sL zD>alNaBmdJ%><$wVZ2{^gTcE`v6a(WWNw(KnXc{7xkPd-JYPuW#rw#o7;L&)!Z_h3 zY+bXHL%mGkp{{?iCo?WP;#tL0`GfocZt|0VHo!Sc z%Jw>IW9yaEV$M7&?lu!}&3P$D@a)M;PA*9Kz+);_m39^h&zVQX-R6dI&3tWA-@(W9 eKhOEM00RK`vkX=t8G%s%0000YCA+x5OwjC)wxS~3Nla#I##CAp+g$1f)Or3l#y;ZbtN1A ziz)l}_PonLM5_lLUU+}c_w)RFe+I-_T3YJlcqrb*Z+jFB25<8pR=h2qC@n2*vRbWF zS67qGW=~dBRMagkEe-t>P*YPgpvB_2-EP*`*Ks%;D5aXp%gbBk`;dUP_5&EC0wsZX zJRXwCB#n)Ygu`J>CKC$_3oI`$M_V#wF zs;ba;kw}D0Cd2&vJc&etnVA{V>GYUr{Q`<=zx8e1Aak*TSm6a8$$mMcu zZf+J@3j_k#!{Z!EMlg|Iy;K%&Gi+~RKmD#6N~H>A zoBOZRdF%k(?S;c$C}|RaM*uDu-qi7Zy8++yuw-@2KIx^?u>p6kfwf+QQhxDKD5`{^ zS%{@VhSv=41{9_p9DB>)k}%doOvorIH;=tzJ=T@T^i7SP}2q z8E@Q!q5r~{By;dn)-@@m55I%NR_L#pciQZEvu`__&N?CP+b{N)00RKLi47*lu|#qJ O0000e3Rtp;oNs-Fc zCIm$Uf56HzA7GU}D5i^z_yIx$?e!P5vi1k8q9|gPkY=M067Ug{cs#S&l_iN2L2%%3 z=FZ%E?!EH>EQ5I*8rXbN_~S?=vbBW5$o_eRWK{*@JE?+_P)Rs?q7c#CtgVaAB42Co z6@lDeBg;94^ZZae5}6XbyG%AN$lfjaaYfb+$er}Jh%ML)v(--(TqOqq*}W!TPsqpZ zxDqcJyqo|4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/TSForestItemData.png b/Templates/BaseGame/game/tools/classIcons/TSForestItemData.png new file mode 100644 index 0000000000000000000000000000000000000000..183c7e53256617e8e579642d5f5489f142fe3f5e GIT binary patch literal 436 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/WaterPlane.png b/Templates/BaseGame/game/tools/classIcons/WaterPlane.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d28f85400956486dd679e4bde74967ba1f63ee GIT binary patch literal 787 zcmV+u1MK{XP)(KK@^_LUbCCTm>6SX z8WpdJHI=qrkSY`@^r6s}Lg`DP?T;+hLZDJXOz{Fb z6%=jF+We-RA&+_8x9C{b-X*z+g? zo)0@m*9u2T_uIa%$kK9JRRlt)$4cm?dvGc2mO~?9vrjPzPbl%UR)A8%;JOm-tUQQd zfCyoQ0Kr~JC37S8VVV1>sWnT8Dwb}ET1!=Vhar&F#Q`3pO4TZsnjXPEksIveLw+!m zbWf_@MP1O%AS0a&-ZU(pk?u+&&V4^RJ#R+Bp>R*&y4m@CMIt>?c1RkHc1I$ux(QUl z0mQAhLNNMnix9h}8%y)|Gm|Oe0YJc&-rhdeJOVQ*B@xDindcb}EnZ4k&U~urjkhnC zM~3>@Rb~_bRpwJMwcap_Wuw^!l7QRA>?-pe$}@yn@$bJW+n!P<2`2E zrjYik{N|CvF@?Y6_70BEXJ>{VKOE-~U#%JWox=J?o;_HZ%lZ{XmU+gkZ97a-EKj{J zUDTdE&Cbn^atJd7pDd0I$5ll>ES8FA<=#*rs3=Cu%5Cn6z8)BQ{dHk}JUx*lOdQQ% zOcL=hxotPlYQ|bF-!v_zwSH{~Jl8*%W_=*jQ^~4kd|uyWpJ521z^|@khwjDtBY`(B zo*Wm;l|Ona5t|%KZtPsa(5p4T*hBblhVT4#s?pt$vr$L4k?8{vi|DTa0{}68QZrmH RaU}o%002ovPDHLkV1gt6V)Xz3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/Zone.png b/Templates/BaseGame/game/tools/classIcons/Zone.png new file mode 100644 index 0000000000000000000000000000000000000000..117e48ad0390a87e7fd7bcd8f0aee81a8ff8dbb1 GIT binary patch literal 406 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggac4;ATohK?Ki-4wxdAc};Se#yZ zdA%RAqrmZx=S?|Sm?Ax$^j9)BI!tS6i4wB0?PUyg6tH37oMy5FNN%-|WArTQICZLP z5&vfQX>XUBJbYgEuco{#O?~w$uW6}CqLp@sWcfat>78E5785t;_sbY~5B)T$JEZjIFLRIdq|5g!O!Q8B#vD|RTQq65$?>Z@bmk`| u@8aIeaFaXsUF(kMJJ+o@Z1MSj(fq!2jPLEWZqdL%WAJqKb6Mw<&;$U@*qTZJ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/cameraSpawn.png b/Templates/BaseGame/game/tools/classIcons/cameraSpawn.png new file mode 100644 index 0000000000000000000000000000000000000000..8a060a6514f75531832bd21e099e72532e4e04f4 GIT binary patch literal 778 zcmV+l1NHogP)`wrnr^*cf2rQQ|4jyCM7r z%wy?+tP@?91|!UN1%*mJD-f}?5XFoqh^hAhq|~&V)pfB((cK2y;QpdYL*2=?qc9t6 zI3O1zN7O?Q1gM#HL{kyWuJ|y}JA&ZE(q~rRL`@`WARopv26mq9IE9+rdf0T;FllX2 zH03DDQc#dp1cQ{1T&)2mjy(7SL1R3!Zcl$fw?t=V+rHC$?KlTl=i7+}d@Ezr)R?PXm=(+#;4|N7#4WqK&ibO`mvD+rt z9A;|Z6iF)j0})11VYBXn&bPPX*4>*tQ%rvHVrDdmY*t&M6w;DK+>%1%Y*RFyInA?C zDaM6MEjZz9qbSA0z$E|h5<7*iGIEGL#%rnh*BC)ArF%aPflbD2c zvRxYiE=NlHrMM<$MybQDXeFariKpY&of`W5cyZwO3Q zZevbV_gtndo$Ml$kj^9(u}y@1^UT8YZko2e_cHy@b^a;90H^^21b^Xd;{X5v07*qo IM6N<$f>Q8lvH$=8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/decal.png b/Templates/BaseGame/game/tools/classIcons/decal.png new file mode 100644 index 0000000000000000000000000000000000000000..ba792f98641a272e6a3b5a6052c3cc9dc2ab05fd GIT binary patch literal 567 zcmV-70?7S|P)BQyy3K41CL8gi^XC}`S}^OS`FcFSZQO^5d+-Z-hxUJUS40p8~tT&4fctM8tn>b!pCCTUoUKs^J^-@Q0g6A`!=b1B&&N z*~yU!b|z(6j!R59kxV2K@cDemWHR3xTb6}Jqmkt#H}}#hu0fJg8dp`-NeVwb%|Wl% z@&0Z?@_0b01c?xlkF#b;hD}ip=2S|h5;&`NdJaWS*|K3876|!_ai+}T%`Iq}2Gcb0 z@bHN9GDSVmk3b*@HW(J6@avE0(XZh3dRd(T!!X%zn5{K(uwfVo24xJG6_luro9We(;+UgY=tp}bwdC>PMB_Mwv9b>f!QXoAJ`CWBlq_j66z z9bBGlvq=QHUTtZ@CNtey-$JlxG-`Kx$@2L;5$_)Y3;<$V{_Skak?8;c002ovPDHLk FV1faY08;<} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/decalNode.png b/Templates/BaseGame/game/tools/classIcons/decalNode.png new file mode 100644 index 0000000000000000000000000000000000000000..f6684f185b3cac485a9bdf9ae09ebcb237c01fd6 GIT binary patch literal 693 zcmV;m0!safP)_QSN*(GI|C(8S<-_C=?3k za=F}V6URMDYr{Ew=%8`1caytC+j@7VmG% z;I*RNZiAO=_hVTR`6|7hH0M{!y^Nb{4g@$ zJP4vJ%lSwoQZt*)9mK#;fg>UtTXzD{W%rNZXXU@^1>Zvs52K&c`u0t z4u=C)t5w&H>o4RfM1)kD{O&(3t`%i&fC)755k7o)=l4$)@QItD| z4oKVOa``^-K_a302=m}xAP|_KeU2Z{<-uTZe1ikyFVM$GrBc%*Pf{2;)9Ex}Z5#aN bw*UhGx+GgiO_=h}00000NkvXXu0mjfGXOk3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/default.png b/Templates/BaseGame/game/tools/classIcons/default.png new file mode 100644 index 0000000000000000000000000000000000000000..0519b522075409e5623fe3805a2d8500b260d1ab GIT binary patch literal 356 zcmV-q0h|7bP)c02>=#c4o(Vi_+`<0eK123~YIppMT#GJ3+iu4hcR zoc8zc-}=9Q{__6$^OxbzUsMc~RUlB+h3y8>wDma7FkBkuSU;z$r1AsQ+2_>8c zFu)A}Ig1PzAPo46&j1FT2HYab3t-KR*bHXu1<_0}FJQxeVfYUU{`~Lnzmg@JXP+hL z0u0;#GkjjNdyykXO2p>J0H^3QplBLKBKptJyJ7#*<7i7id()YjqDpwL|ZK(nAjlblQ_`9KD(tCYG&sv{m=GQ%Ylx2k`qJRZAA}N3L^k$#jl8e>_3N?#eGU}r?J0?-Y zX}v20J}Vw>gxl2tO$MKY9OQ!G3gDT^Zd}$A8)1O8>kg?}jGt%boC>KX>?EN;g=fIM z)`x&JwIzNxOI=Fj!s3ivFkn$YLBnj1h2tjkI3_u^!)4<(C5l}y34;ZKqBLDERt(i9ddhIyfDIIWap}1Z*ZH`N{ohYCOpzhrB5Sc%5R=c=X9HS z`jL+UJxE^;ctp1W3>ge73nf0fs2&NsU{OHFkgQW;HHrsRMKeKhwiq1+w8ezYe-$}M zF90PU7E@{ejbOk!G20kVG-;g*HY1{t9moX()^1`JQ4~J@N{bYUK%*eH z42o)zHWi1cC7W6TaV~K7FK7;GY6yo2CvB*fM1z8$AR{4#C?g~vIEb%E(X9Jj-+eqK zTIz$#z3-g!oqNu?-_ubK*6GB@wh&v`U5dHLR)($E4lQQ1@j4CB&bB>^)fAi$jnhQH zK~A@o`^tHl3Mq*$W6xBVn@MI0vqV)jfkIBZ1CH6{&1te&jr0@ha*Gb5ca%>_S@!8i z5(69_T03B5-|F@p8O?ege6jmTm8W--3Nl%2CcfaCS{flKFFTpfeMS3m9eQSErz0t^ zdNarm=M~;Yz1HM5=cJ8Xo*^ECO11)LXvdH1Lg-nLZ<8fX?ivv0a9^X`(JMbA794Pq zz=xXzvWb#BI2E}dm(S916vGZXHZGejkG7yOXdroS9e$jROMJ0>U z(GFe$P&gSRh;nlQMozu75Ou512oAVHi3t`IgphiHn^C?N@r@g(G0Bv; yB8C`-JmC1&Qd5`IP#^z5@CJvgbe$Z~OtJrAt1|RRq zxSV8IIRS!0>gIWkpw(J~qTou&r&u{jsAHD@fO~$^1K;<6b8cwOsR3gQ^8D*~dt)!Y zf4Uj|P(W}GP3XD}a4^v(S(ZbZev;cqIwx!jhQ*}MGzxNOw}a!mRz#AdaCaL6c30RH z{ZMeB_}ZS05Wz>OV9DGi?&VupfI;N|(Xa($CY22u1dyH>r+BJwT30Q`un-H@oVzW@LL M07*qoM6N<$g2Vy0TmS$7 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/particleEffecterObject.png b/Templates/BaseGame/game/tools/classIcons/particleEffecterObject.png new file mode 100644 index 0000000000000000000000000000000000000000..255a5a8a8a380d988090177168050f6ce1a9065d GIT binary patch literal 727 zcmV;|0x127P)un+_ebn|k2mJG}4nyzlqD@5h1GxEDY<{taI!LVk4Llx7i7;Ail_ z562LVEF!uVd;dXGDL1(}b>{j3GRT@edTD4;=c-A}x(K?`R7ht-~lKnt)BVedm5^XpHm!a)*FuFue5pd6A z#wMyXM~z)R^q7IZ2drTjh)n{wx)S6`!`<$0vlvQ7PkBTl!UW*)%hgT^djX6M0Po%a zWo$+=wu)*!=6+$)A4YI>6;jH`);oh9w@4D3r~QCe476DRCIZY&fX7`4n3e&-p#6KE zX8NO8rBEZ*bS}hH9kGeLBg)L%M^(QamS{(?S0CbJg{rG`t~3GCUE0k#zJj`XYv4jF z^D_jmX98ZDuMsL+x^rl^O)MRyeeKWsOP+Lo*iQN;Ed$5cYVHkeCvz@c!&dxTM z&g0t3X{D$w%SegH24N`HRT0q-fn9XdO&4`j&_x$rM9@v)O+Qo@k~dKl30Xl{EHok% zLL4>I>5g;rtht}FbG{CuvV;VC;N|_k7v6{GecqQ~6<+TsspnMMI6A=&+)Jh6Ljc7Z zFs&~1v}q?Ki+)SBlS~uK+gepN(yyx8zX+J(PcuMDRC-pl=#GG&RQMWgjsDHnqjyBn zv8Do}k?|Puz7s*tCSJzLZkUHZJCpTwFL~bT(ek=)S6UlSs;U}YMPSyC;DM2=2++1z zIero72!aa@)HRKS9#Mu#$0SSKYc^H?8nxU&E|)F#g^@G3PAN)p;2_ZV3Ftfw=<~pp zZs6`I5U(Hz<+cf>zLhp_a_=*G$X_G84MVY-@w_1!ZfDYyZfutff@wgT0xsOgWQ_m* zPU`6M?rn;&iqcS<%0!3Fe;`t4E3Sq(3s#el*HFgomD1^*GL(HP5PJULL{O;9F&@`D#L}3h!@~Wgg2V9ak{m=CF0##6Uio0Hm4-) zH&j?x)@Udoha~gvP|Dsvk+&-aV%1`0wO^I;>JRDoZ!sE{86;*;aC`eWZ{ROf;h^a>gUi;3mO3V9YIf{M;T!&js@t z-IvXhSL-5J#$(A=Wj51u`8@Ib7r~Mm7thm6e*_o+O^6B%Ny$dN00000NkvXXu0mjf DP8MaB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/particleSimulation.png b/Templates/BaseGame/game/tools/classIcons/particleSimulation.png new file mode 100644 index 0000000000000000000000000000000000000000..95bcfef6b6e184a1cf0d1b2d61160325b08a1553 GIT binary patch literal 766 zcmVzGdSf; z1g zG;Opuzuvp=b>7n%Cl}R)Gw;m3=R5bi-#LfP2N1^k+MckA{@+$I$ck~2{o@XI<5&1R z78v_?0)@=KfnlW#+#S7t#7gh_8%@QN)f?O6f*@FoG4SaLWNu$8#xTpWu%NQA($fQ} zskwMynkHOb$IqIQnp-?`w@+0RQ<5YTX$|_5N6|*A(N^Qva4Pv>*YIyp9eZFI)4pU<-8@U*}UwF zFZD);gO+v4VPL1qx%?@0ITC`)MSxqRXb=dIaW`=F9IzULbmQp`{QN+w+gM@bY7#TKWOfk`hBC5dPJ4YSThAPtO02>gb8 z&DmOZ2a<}a8e0^_P#s7~6*%XPvfku}YLwIVl>y+>;@aUZm-DGi&s3&F@v|rhT-uz# zE+f?uvbI!#w1Lz#W}<-;Z|&~e7k|}U0j;V$pYqrj*-cjL#L`xtzTS`8;jY#hPu=%P zZ__LYEX%TKYgG3&QtwNB36A;N1{?0(%&P)8Di-sC0GIS7EDnqyPW_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/pathMarker.png b/Templates/BaseGame/game/tools/classIcons/pathMarker.png new file mode 100644 index 0000000000000000000000000000000000000000..a93b82252b5d4ec3e48c2899dac5961e3984b260 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XyggkULn>}1{rUgjp4pWlg~85Qj$!J9%cqYxC`z!g860Ao zFma-w^QWaeN(@Km8yF;UyND?{-4^Wl{&+(Ak{hgxUpCBonQ$P6K_G!4jKQetXXBYe QKw}v^UHx3vIVCg!05zaE0RR91 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/classIcons/volumeLight.png b/Templates/BaseGame/game/tools/classIcons/volumeLight.png new file mode 100644 index 0000000000000000000000000000000000000000..114a7f2abafd80f9b23e226ad11a768434194f22 GIT binary patch literal 665 zcmV;K0%rY*P)F8QMC}R~d&@3+KlUY84AvmLZOO_s!dn zSDIw5)sO#5kue0LW*a!G+i*$-!Z?A@2wp17Ph3rXm_ecD>n4{FyN-$TM>{B6I+lK- zfRiLe5asz6Kwg+~O|x9VK4m{`I4GF~+^=Gg7=D-_PBM%a!3H4RDwMh4c~rCTyy?I! zXc)~yXa#{)7$b;NwvnPY_O<~Syigg05NPaKxNJGlH4Xjy5IT#)D8+OcqriMP4=|X8 z8-Qe8EMJ}+6j^L56_UlEZrNUM;u8>v;tV%@zVPxH=s5rrUEoQ*glA2g_X7QykBX(K z=hwqrZqd|nM@9Wg^p%GdPQE(~a5szKmUY#8hhXznLKm`) zf)|f%oRZxOG7c~|;_d%-O z|IxzSqrYKB?@cxFZ@m@T10O9qVwVbtIAwW1Sw_koA%1W4T*36y=buWdw6Qz(@xAA% ze1FG)+*Tv9Un}-ZvHN7dZFkfkDgO6&{w2Twi0(5Gy)VzK00000NkvXXu0mjfjPNdI literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/componentEditor/gui/superToolTipDlg.ed.gui b/Templates/BaseGame/game/tools/componentEditor/gui/superToolTipDlg.ed.gui new file mode 100644 index 000000000..ef506941a --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/gui/superToolTipDlg.ed.gui @@ -0,0 +1,45 @@ +%guiContent = new GuiControl(SuperTooltipDlg) { + canSaveDynamicFields = "0"; + Profile = "GuiTransparentProfileModeless"; + class = "SuperTooltip"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiControl(SuperTooltipWindow) { + canSaveDynamicFields = "0"; + Profile = "EditorTextEditBoldModeless"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "216 160"; + Extent = "221 134"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + internalName = "tooltipWindow"; + + new GuiMLTextCtrl(SuperTooltipMLText) { + canSaveDynamicFields = "0"; + Profile = "EditorMLTextProfileModeless"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 5"; + Extent = "210 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + internalName = "tooltipMLText"; + }; + }; +}; + diff --git a/Templates/BaseGame/game/tools/componentEditor/main.cs b/Templates/BaseGame/game/tools/componentEditor/main.cs new file mode 100644 index 000000000..56d74830a --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/main.cs @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//Scripts +exec("./scripts/componentEditor.ed.cs"); +exec("./scripts/superToolTipDlg.ed.cs"); + +//gui +exec("./gui/superToolTipDlg.ed.gui"); diff --git a/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs b/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs new file mode 100644 index 000000000..9a9ce33d6 --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/scripts/componentEditor.ed.cs @@ -0,0 +1,233 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function GuiInspectorEntityGroup::CreateContent(%this) +{ +} + +function GuiInspectorEntityGroup::InspectObject( %this, %targetObject ) +{ + %this.stack.clear(); + %this.stack.addGuiControl(%this.createAddComponentList()); +} + +function GuiInspectorEntityGroup::createAddComponentList(%this) +{ + %extent = %this.getExtent(); + + %container = new GuiControl() + { + Profile = "EditorContainerProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = %extent.x SPC "25"; + }; + + %componentList = new GuiPopUpMenuCtrlEx(QuickEditComponentList) + { + Profile = "GuiPopupMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "28 4"; + Extent = (%extent.x - 28) SPC "18"; + hovertime = "100"; + tooltip = "The component to add to the object"; + tooltipProfile = "EditorToolTipProfile"; + }; + + %addButton = new GuiIconButtonCtrl() { + class = AddComponentQuickEditButton; + Profile = "EditorButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "2 0"; + Extent = "24 24"; + buttonMargin = "4 4"; + iconLocation = "Left"; + sizeIconToButton = "0"; + iconBitmap = "tools/gui/images/iconAdd.png"; + hovertime = "100"; + tooltip = "Add the selected component to the object"; + tooltipProfile = "EditorToolTipProfile"; + componentList = %componentList; + }; + + %componentList.refresh(); + + %container.add(%componentList); + %container.add(%addButton); + + if(!isObject("componentTooltipTheme")) + { + %theme = createsupertooltiptheme("componentTooltipTheme"); + %theme.addstyle("headerstyle", ""); + %theme.addstyle("headertwostyle", ""); + %theme.addstyle("basictextstyle", ""); + %theme.setdefaultstyle("title", "headerstyle"); + %theme.setdefaultstyle("paramtitle", "headertwostyle"); + %theme.setdefaultstyle("param", "basictextstyle"); + %theme.setspacing(3, 0); + } + + return %container; +} + +function QuickEditComponentList::refresh(%this) +{ + %this.clear(); + + //find all ComponentAssets + %assetQuery = new AssetQuery(); + if(!AssetDatabase.findAssetType(%assetQuery, "ComponentAsset")) + return; //if we didn't find ANY, just exit + + // Find all the types. + %count = %assetQuery.getCount(); + + %categories = ""; + for (%i = 0; %i < %count; %i++) + { + %assetId = %assetQuery.getAsset(%i); + + %componentAsset = AssetDatabase.acquireAsset(%assetId); + %componentType = %componentAsset.componentType; + if (!isInList(%componentType, %categories)) + %categories = %categories TAB %componentType; + } + + %categories = trim(%categories); + + %index = 0; + %categoryCount = getFieldCount(%categories); + for (%i = 0; %i < %categoryCount; %i++) + { + %category = getField(%categories, %i); + %this.addCategory(%category); + + for (%j = 0; %j < %count; %j++) + { + %assetId = %assetQuery.getAsset(%j); + + %componentAsset = AssetDatabase.acquireAsset(%assetId); + %componentType = %componentAsset.componentType; + %friendlyName = %componentAsset.friendlyName; + + if (%componentType $= %category) + { + //TODO: Haven't worked out getting categories to look distinct + //from entries in the drop-down so for now just indent them for the visual distinction + %spacedName = " " @ %friendlyName; + %this.add(%spacedName, %index); + %this.component[%index] = %componentAsset; + %index++; + } + } + } +} + +function QuickEditComponentList::onHotTrackItem( %this, %itemID ) +{ + %componentObj = %this.component[%itemID]; + if( isObject( %componentObj ) && %this.componentDesc != %componentObj ) + { + SuperTooltipDlg.init("componentTooltipTheme"); + SuperTooltipDlg.setTitle(%componentObj.friendlyName); + SuperTooltipDlg.addParam("", %componentObj.description @ "\n"); + + %fieldCount = %componentObj.getComponentFieldCount(); + for (%i = 0; %i < %fieldCount; %i++) + { + %name = getField(%componentObj.getComponentField(%i), 0); + + SuperTooltipDlg.addParam(%name, %description @ "\n"); + } + %position = %this.getGlobalPosition(); + SuperTooltipDlg.processTooltip( %position,0,1 ); + %this.opened = true; + %this.componentDesc = %componentObj; + } + else if( !isObject( %componentObj ) ) + { + if( %this.opened == true ) + SuperTooltipDlg.hide(); + %this.componentDesc = ""; + } +} + +function QuickEditComponentList::setProperty(%this, %object) +{ + %this.objectToAdd = %object; +} + +function QuickEditComponentList::onSelect(%this) +{ + if( %this.opened == true ) + SuperTooltipDlg.hide(); + + %this.componentToAdd = %this.component[%this.getSelected()]; +} + +function QuickEditComponentList::onCancel( %this ) +{ + if( %this.opened == true ) + SuperTooltipDlg.hide(); +} + +function AddComponentQuickEditButton::onClick(%this) +{ + %component = %this.componentList.componentToAdd; + + %componentName = %this.componentList.componentToAdd.componentName; + %componentClass = %this.componentList.componentToAdd.componentClass; + + %command = "$ComponentEditor::newComponent = new" SPC %componentClass SPC "(){ class = \"" + @ %componentName @ "\"; };"; + + eval(%command); + + %instance = $ComponentEditor::newComponent; + %undo = new UndoScriptAction() + { + actionName = "Added Component"; + class = UndoAddComponent; + object = %this.componentList.objectToAdd; + component = %instance; + }; + + %undo.addToManager(LevelBuilderUndoManager); + + %instance.owner = Inspector.getInspectObject(0); + %instance.owner.add(%instance); + + Inspector.schedule( 50, "refresh" ); + EWorldEditor.isDirty = true; +} + +function addComponent(%obj, %instance) +{ + echo("Adding the component!"); + %obj.addComponent(%instance); + Inspector.schedule( 50, "refresh" ); + EWorldEditor.isDirty = true; +} + diff --git a/Templates/BaseGame/game/tools/componentEditor/scripts/superToolTipDlg.ed.cs b/Templates/BaseGame/game/tools/componentEditor/scripts/superToolTipDlg.ed.cs new file mode 100644 index 000000000..7f25bd5e6 --- /dev/null +++ b/Templates/BaseGame/game/tools/componentEditor/scripts/superToolTipDlg.ed.cs @@ -0,0 +1,155 @@ +function createSuperTooltipTheme(%name) +{ + %theme = new ScriptObject() + { + class = SuperTooltipTheme; + }; + + %theme.setName(%name); + + return %theme; +} + +function SuperTooltipTheme::addStyle(%this, %name, %style) +{ + %this.styles[%name] = %style; +} + +function SuperTooltipTheme::setDefaultStyle(%this, %type, %default) +{ + %this.defaultStyles[%type] = %default; +} + +function SuperTooltipTheme::setSpacing(%this, %verticalSpace, %horizontalSpace) +{ + %this.verticalSpace = %verticalSpace; + %this.horizontalSpace = %horizontalSpace; +} + +function SuperTooltipTheme::getStyle(%this, %name) +{ + return %this.styles[%name]; +} + +function SuperTooltip::init(%this, %theme) +{ + %this.clearTooltip(); + + if(isObject(%theme)) + %this.setTheme(%theme); +} + +function SuperTooltip::clearTooltip(%this) +{ + if(%this.paramCount > 0) + { + for(%i=0;%i<%this.paramCount;%i++) + %this.param[%i] = ""; + } + + %this.title = ""; + %this.paramCount = 0; +} + +function SuperTooltip::processTooltip(%this, %globalPos, %verticalAlign, %horizontalAlign) +{ + if (%verticalAlign $= "") + %verticalAlign = 1; + if (%horizontalAlign $= "") + %horizontalAlign = 0; + + %tooltipWindow = %this.findObjectByInternalName("tooltipWindow"); + + if(isObject(%tooltipWindow)) + %tooltipMLText = %tooltipWindow.findObjectByInternalName("tooltipMLText"); + else + return false; + + if(!isObject(%tooltipMLText)) + return false; + + %verticalSpace = %this.theme.verticalSpace; + %horizontalSpace = %this.theme.horizontalSpace; + + if (%verticalAlign == 1) + %verticalSpace = -%verticalSpace; + if (%horizontalAlign == 1) + %horizontalSpace = -%horizontalSpace; + + %text = %this.getFormatedText(); + %tooltipMLText.setText(%text); + + canvas.pushDialog(%this); + + %tooltipMLText.forceReflow(); + %MLExtent = %tooltipMLText.extent; + %MLHeight = getWord(%MLExtent, 1); + + %tooltipExtent = %tooltipWindow.extent; + %tooltipWidth = getWord(%tooltipExtent, 0); + %tooltipHeight = %MLHeight; + %tooltipWindow.extent = %tooltipWidth SPC %tooltipHeight; + + %globalPosX = getWord(%globalPos, 0); + %globalPosY = getWord(%globalPos, 1); + + %tooltipPosX = %globalPosX - (%horizontalAlign * %tooltipWidth) + %horizontalSpace; + %tooltipPosY = %globalPosY - (%verticalAlign * %tooltipHeight) + %verticalSpace; + + %tooltipWindow.setPosition(%tooltipPosX, %tooltipPosY); + + return true; +} + +function SuperTooltip::hide(%this) +{ + canvas.popDialog(%this); + + %this.clearTooltip(); +} + +function SuperTooltip::setTheme(%this, %theme) +{ + %this.theme = %theme; +} + +function SuperTooltip::setTitle(%this, %title, %style) +{ + if(%style !$= "") + %themeStyle = %this.theme.styles[%style]; + else + %themeStyle = %this.theme.getStyle(%this.theme.defaultStyles[Title]); + + %this.title = %themeStyle @ %title; +} + +function SuperTooltip::addParam(%this, %title, %text, %paramTitleStyle, %paramStyle) +{ + if(%paramTitleStyle !$= "") + %themeTitleStyle = %this.theme.styles[%paramTitleStyle]; + else + %themeTitleStyle = %this.theme.getStyle(%this.theme.defaultStyles[ParamTitle]); + + if(%paramStyle !$= "") + %themeStyle = %this.theme.styles[%paramStyle]; + else + %themeStyle = %this.theme.getStyle(%this.theme.defaultStyles[Param]); + + if (%title $= "") + %this.param[%this.paramCount] = %themeStyle @ %text @ "\n"; + else + %this.param[%this.paramCount] = %themeTitleStyle @ %title @ ": " @ %themeStyle @ %text @ "\n"; + %this.paramCount++; +} + +function SuperTooltip::getFormatedText(%this) +{ + %text = %this.title @ "\n\n"; + + for(%i=0;%i<%this.paramCount;%i++) + { + %text = %text @ %this.param[%i]; + } + + return %text; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditor.cs b/Templates/BaseGame/game/tools/convexEditor/convexEditor.cs new file mode 100644 index 000000000..859667bc1 --- /dev/null +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditor.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile( ConvexEditorProfile ) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192 192"; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiDisabledTextEditProfile) +{ + opaque = false; + border = 0; + bitmap = "./textEdit"; + borderColor = "255 255 255 200"; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fontColorNA = "128 128 128"; + textOffset = "4 2"; + autoSizeWidth = false; + autoSizeHeight = false; + tab = false; + canKeyFocus = false; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiSimpleBorderProfile) +{ + opaque = false; + border = 1; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.cs b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.cs new file mode 100644 index 000000000..1cda5416c --- /dev/null +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function ConvexEditorGui::onWake( %this ) +{ +} + +function ConvexEditorGui::onSleep( %this ) +{ +} + +function ConvexEditorGui::createConvexBox( %this ) +{ + %obj = genericCreateObject( "ConvexShape" ); + %this.handleDeselect(); + %this.selectConvex( %obj ); + %this.dropSelectionAtScreenCenter(); +} + +function ConvexEditorGui::onSelectionChanged( %this, %shape, %face ) +{ + //echo( "onSelectionChanged: " @ %shape SPC %face ); + + ConvexEditorSplitFaceBtn.setActive( false ); + ConvexEditorSplitFaceBtn.ToolTip = "Split selected face [Disabled]" NL "Use Ctrl + Rotate instead for more control"; + ConvexEditorDeleteFaceBtn.setActive( false ); + ConvexEditorDeleteFaceBtn.ToolTip = "Delete selection [Disabled] (Delete)"; + + if ( !isObject( %shape ) ) + return; + + ConvexEditorDeleteFaceBtn.setActive( true ); + + if ( %face == -1 ) + ConvexEditorDeleteFaceBtn.ToolTip = "Delete selected ConvexShape (Delete)"; + else + { + ConvexEditorDeleteFaceBtn.ToolTip = "Delete selected Face (Delete)"; + + ConvexEditorSplitFaceBtn.ToolTip = "Split selected face" NL "Use Ctrl + Rotate instead for more control"; + ConvexEditorSplitFaceBtn.setActive( true ); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.gui b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.gui new file mode 100644 index 000000000..1849a91c5 --- /dev/null +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditorGui.gui @@ -0,0 +1,440 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiConvexEditorCtrl(ConvexEditorGui) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ConvexEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + DefaultWidth = "10"; + HoverSplineColor = "0 255 0 255"; + SelectedSplineColor = "255 0 255 255"; + HoverNodeColor = "255 255 255 255"; + + new GuiWindowCollapseCtrl(ConvexEditorTreeWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 167"; + MinExtent = "210 100"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EditorGui.setEditor( WorldEditorInspectorPlugin );"; + EdgeSnap = "1"; + text = "ConvexShapes"; + + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "5 25"; + Extent = "200 120"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 118"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(ConvexTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "1"; + internalNamesOnly = "0"; + }; + }; + }; + }; + new GuiWindowCollapseCtrl(ConvexEditorOptionsWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(ConvexEditorTreeWindow.extent, 1) - 2; + Extent = "210 530"; + MinExtent = "210 298"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EditorGui.setEditor( WorldEditorPlugin );"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(){ //Node Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "202 85"; + Docking = "Top"; + Margin = "3 3 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "86 18"; + text = "Node Properties"; + }; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 21"; + Extent = "46 18"; + text = "Position"; + }; + new GuiTextEditCtrl(){ + internalName = "position"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 21"; + Extent = "141 18"; + text = ""; + AltCommand = "ConvexEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 42"; + Extent = "46 18"; + text = "Rotation"; + }; + new GuiTextEditCtrl(){ + internalName = "rotation"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 42"; + Extent = "141 18"; + text = ""; + AltCommand = "ConvexEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 63"; + Extent = "46 18"; + text = "Width"; + }; + new GuiTextEditCtrl(){ + internalName = "width"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "57 63"; + Extent = "52 18"; + text = ""; + AltCommand = "ConvexEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "110 63"; + Extent = "32 18"; + text = "Depth"; + }; + new GuiTextEditCtrl(){ + internalName = "depth"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "146 63"; + Extent = "52 18"; + text = ""; + AltCommand = "ConvexEditorGui.editNodeDetails();"; + }; + }; + new GuiContainer(){ //Conve Road Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 112"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "ConvexShape Properties"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 129"; + Extent = "202 357"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "-14 41 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(ConvexInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + name = "ConvexInspector"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "179 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + }; + }; + new GuiMLTextCtrl(ConvexFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 485"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + new GuiWindowCollapseCtrl(ConvexEditorTipsWindow) { + CollapseGroup = "-1"; + CollapseGroupNum = "-1"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Tips"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowCollapseProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + position = "6 483"; + Extent = "136 246"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Docking = "Client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 24"; + Extent = "128 218"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl() { + columns = "0"; + fitParentWidth = "1"; + clipColumnText = "0"; + isContainer = "1"; + Profile = "ToolsGuiTextListProfile"; + HorizSizing = "width"; + VertSizing = "top"; + position = "1 1"; + Extent = "126 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "TextList"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditorSettingsTab.ed.gui b/Templates/BaseGame/game/tools/convexEditor/convexEditorSettingsTab.ed.gui new file mode 100644 index 000000000..3cd3a1ebd --- /dev/null +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditorSettingsTab.ed.gui @@ -0,0 +1,180 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ConvexEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EConvexEditorSettingsPage) { + fitBook = "1"; + text = "Convex Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Defaults"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Material:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ConvexEditorPlugin.readSettings();"; + editorSettingsValue = "ConvexEditor/MaterialName"; + editorSettingsWrite = "ConvexEditorPlugin.writeSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/convexEditor/convexEditorToolbar.ed.gui b/Templates/BaseGame/game/tools/convexEditor/convexEditorToolbar.ed.gui new file mode 100644 index 000000000..575907d81 --- /dev/null +++ b/Templates/BaseGame/game/tools/convexEditor/convexEditorToolbar.ed.gui @@ -0,0 +1,121 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(convexEditorToolbar, EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = ""; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "305 0"; + Extent = "550" SPC getWord(EditorGuiToolbar.extent, 1); + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + EdgeSnap = "0"; + text =""; + + new GuiContainer() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "menubarProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 32"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "15 7"; + extent = "86 16"; + minExtent = "8 8"; + visible = "1"; + text = "Sketch Tool"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiBitmapCtrl() { + Profile = "ToolsGuiDefaultProfile"; + position = "94 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl(ConvexEditorCreateBoxBtn) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "100 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ConvexEditorGui.createConvexBox();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Create ConvexShape Box" NL "Use Alt + Click-Drag instead of this for more control of starting placement."; + hovertime = "1000"; + bitmap = "tools/convexEditor/images/convex-editor-btn"; + text = ""; + groupNum = "-1"; + buttonType = "pushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ConvexEditorSplitFaceBtn) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "134 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ConvexEditorGui.splitSelectedFace();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Split selected face" NL "Use Ctrl + Rotate instead for more control."; + hovertime = "1000"; + bitmap = "tools/convexEditor/images/split-face-btn"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ConvexEditorDeleteFaceBtn) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "166 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ConvexEditorGui.handleDelete();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete selected face" NL "(Delete)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/delete-btn"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; +}; diff --git a/Templates/BaseGame/game/tools/convexEditor/images/512_orange.png b/Templates/BaseGame/game/tools/convexEditor/images/512_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..43c4953e82e8695d459a043f220cf5e905d1ad7b GIT binary patch literal 6295 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/convexEditor/images/convex-editor-btn_n.png b/Templates/BaseGame/game/tools/convexEditor/images/convex-editor-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..cdb2a4b6da16c9c3617503a23e5fd324bd30114f GIT binary patch literal 603 zcmV-h0;K(kP)$JD8FPhC{;UtxU`};dv zkk{*ldY=zmE*BVv!6xZJl}M(e^vbew+Z+l7QBWx4A(c!*E|-I+$44IMt-pN5@S}<- zc7yH+O6$aC3_hP9y223@KqjL>M$>qpa-_*Ai|5bmUbR|hV`CE!WDQO9$eexgWSEs} z=>(b(rAd;Mt_7|Iz2yxkipLJB2{~Puor&?rJRapsDYh$vs$hl>31)~ur)#CTO046q z?u|z`)+m+SpCci*vK9S67#v*j{tH-K-+b=kxg< z0-EIP2~|Kn5Lno$b%M;bLM2Y7K7$+;Njmkxr{6Lh$EGEP3Qa<?P)Z8GHbpB}7Y$Tp|%fet?da!f8wcHV-LYZ|ogr?(W*_ zS9aIIBdvUQJ$uibnR91HwOXwQ0NQ+&umMv_!8A=+mIcRlI<{jsEdeaJPN`h3pja%z zFbr(UdcB{eAH9BCKsDg~S`D|iwsb_Bve9USPiJQ(O-mSV8cc{YC?xH^dd!D+q#gHFH*TgYNF_ z;h=FKucQ8?jz>E?7}+O2TF*sCkncighL`(R@>Q&kCGdKJ&y^(ApA;npU z6-|_ld1~)H;yx;EO}dO6`q;j{{D5cgzpOVwm}WXznJi?YbDZh3hi~_}*#N! zF-uYWR}FcVWu|}K*a?MOg@v{@4!G6vSI6MMO~YIWRNh;~Rr=jWeGw(al~Zo|}RQP_ZWWX$>} ZzyRq!;v)l|Irsno002ovPDHLkV1i+TR?`3g literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_h.png b/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d84b49d0074358911b19ca7ae228d83225fef3 GIT binary patch literal 915 zcmV;E18n?>P)st5Ee;vBi5OpcF6Sy`mna=*ffNk0=y+6pFtpoAw#Rsz*(bYEIp-km9x_Q=55()8Zq;;qlMTHR^4k9lwsjJAO|geMdZ zN~y8>{ngdO8w(4ch~>2_S9ce0Fa568>Ta{ybo@?-k7Wn(P+o0(%K3OsU3>cEOMbh+ z?mk-i=KCEd$Ck-Fs2X=90vnzEGIY9Wk7&5_vonVxvr}Vr%#;-;Xnpca&87wZfQV7G2xrkhSOTD zmYkZ(2l<@GJ!a2?NQ>9z}F0x3If2^*sEe49c5G znY4EC<;Hru-F6zymgoDvj6ZDD*XAF;d7ZW(cC6pPJ(e zWP(!BrtZq#_dyV70Q)15Ql=V6vrB7i?fr+7AX0>6#E#qS!Hhw*QLhyXg~G&7gN;Um pib<6MX|qzPl(GM}6aF6o1^_%4$;a*<2nhfH002ovPDHLkV1l-Rw)6l1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_i.png b/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_i.png new file mode 100644 index 0000000000000000000000000000000000000000..c8719cc7de3b4a11471af41d0d64b3151a2ff156 GIT binary patch literal 425 zcmV;a0apHrP)G@yNfR51MzSvFNbl+0ou7kBDe(O; z_=9JENQLd=q3o^Y9Vxqrc%bKiB2bLyQ8cw-1X0*bDG?<^v@q#{bMd0L&2m%TSs1<( zajaD`#*^o#;KN{`)1DIX>+t*ni15uxPcN^oPqH+FEYmB6l%mRWkUa_g)3akC1TfBl zGY0w{Dc;@QOuS6*a;U!3ZRKWlfUEN5G_O*=tobpVGc&4qw&N2y5d@(PC?QuM9x%L*kX8{l>S+}y+Q40zgKE8}|LPa?Q-A>gLjHM5 Tt_}rz00000NkvXXu0mjf8RETS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_n.png b/Templates/BaseGame/game/tools/convexEditor/images/split-face-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1009525714213eaef2ec20b6888c0d61397992b2 GIT binary patch literal 543 zcmV+)0^t3LP)J$Kv0LnbTQgv1&>9OmKm$%ntnc}s+-1EbO6yiqz_O(cB zMR50OR*#5qQJ$})xJt+IlUA=YF#cLh^EkadkcW_b#di&c6fCC0JxPhK<2WlI$q2@I z0fm0v-x|Q$d-f3t0X>D#mjWVzAhjEY&p9r|)czZh$*{uGqQ-k=k3q8^Y3aGnc7hUE zEHc2Hv=d|aJqu{8*x)$Jh1^>mRAIHl_G9auFolJgw$g)I{;mu%46$bG+s1_tCh}dy zklMtWtPul%+Bgxzq75hm)w+WorldEditorToolbar.setVisible(false); + EditorGui.bringToFront( DatablockEditorPlugin ); + + DatablockEditorTreeWindow.setVisible( true ); + DatablockEditorInspectorWindow.setVisible( true ); + DatablockEditorInspectorWindow.makeFirstResponder( true ); + + %this.map.push(); + + // Set the status bar here until all tool have been hooked up + EditorGuiStatusBar.setInfo( "Datablock editor." ); + + %numSelected = %this.getNumSelectedDatablocks(); + if( !%numSelected ) + EditorGuiStatusBar.setSelection( "" ); + else + EditorGuiStatusBar.setSelection( %numSelected @ " datablocks selected" ); + + %this.init(); + DatablockEditorPlugin.readSettings(); + + if( EWorldEditor.getSelectionSize() == 1 ) + %this.onObjectSelected( EWorldEditor.getSelectedObject( 0 ) ); + + Parent::onActivated( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::onDeactivated( %this ) +{ + DatablockEditorPlugin.writeSettings(); + + DatablockEditorInspectorWindow.setVisible( false ); + DatablockEditorTreeWindow.setVisible( false ); + %this.map.pop(); + + Parent::onDeactivated(%this); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::onExitMission( %this ) +{ + DatablockEditorTree.clear(); + DatablockEditorInspector.inspect( "" ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::openDatablock( %this, %datablock ) +{ + EditorGui.setEditor( DatablockEditorPlugin ); + %this.selectDatablock( %datablock ); + DatablockEditorTreeTabBook.selectedPage = 0; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::setEditorFunction( %this ) +{ + return true; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::onObjectSelected( %this, %object ) +{ + // Select datablock of object if this is a GameBase object. + + if( %object.isMemberOfClass( "GameBase" ) ) + %this.selectDatablock( %object.getDatablock() ); + else if( %object.isMemberOfClass( "SFXEmitter" ) && isObject( %object.track ) ) + %this.selectDatablock( %object.track ); + else if( %object.isMemberOfClass( "LightBase" ) && isObject( %object.animationType ) ) + %this.selectDatablock( %object.animationType ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::populateTrees(%this) +{ + // Populate datablock tree. + + if( %this.excludeClientOnlyDatablocks ) + %set = DataBlockGroup; + else + %set = DataBlockSet; + + DatablockEditorTree.clear(); + + foreach( %datablock in %set ) + { + %unlistedFound = false; + %id = %datablock.getId(); + + foreach( %obj in UnlistedDatablocks ) + if( %obj.getId() == %id ) + { + %unlistedFound = true; + break; + } + + if( %unlistedFound ) + continue; + + %this.addExistingItem( %datablock, true ); + } + + DatablockEditorTree.sort( 0, true, false, false ); + + // Populate datablock type tree. + + %classList = enumerateConsoleClasses( "SimDatablock" ); + DatablockEditorTypeTree.clear(); + + foreach$( %datablockClass in %classList ) + { + if( !%this.isExcludedDatablockType( %datablockClass ) + && DatablockEditorTypeTree.findItemByName( %datablockClass ) == 0 ) + DatablockEditorTypeTree.insertItem( 0, %datablockClass ); + } + + DatablockEditorTypeTree.sort( 0, false, false, false ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::addExistingItem( %this, %datablock, %dontSort ) +{ + %tree = DatablockEditorTree; + + // Look up class at root level. Create if needed. + + %class = %datablock.getClassName(); + %parentID = %tree.findItemByName( %class ); + if( %parentID == 0 ) + %parentID = %tree.insertItem( 0, %class ); + + // If the datablock is already there, don't + // do anything. + + if( %tree.findItemByValue( %datablock.getId() ) ) + return; + + // It doesn't exist so add it. + + %name = %datablock.getName(); + if( %this.PM.isDirty( %datablock ) ) + %name = %name @ " *"; + + %id = DatablockEditorTree.insertItem( %parentID, %name, %datablock.getId() ); + if( !%dontSort ) + DatablockEditorTree.sort( %parentID, false, false, false ); + + return %id; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::isExcludedDatablockType( %this, %className ) +{ + switch$( %className ) + { + case "SimDatablock": + return true; + case "SFXTrack": // Abstract. + return true; + case "SFXFMODEvent": // Internally created. + return true; + case "SFXFMODEventGroup": // Internally created. + return true; + } + return false; +} + +//============================================================================================= +// Settings. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup("DatablockEditor", true); + + EditorSettings.setDefaultValue("libraryTab", "0"); + + EditorSettings.endGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup("DatablockEditor", true); + + DatablockEditorTreeTabBook.selectPage( EditorSettings.value( "libraryTab" ) ); + %db = EditorSettings.value( "selectedDatablock" ); + if( isObject( %db ) && %db.isMemberOfClass( "SimDatablock" ) ) + %this.selectDatablock( %db ); + + EditorSettings.endGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "DatablockEditor", true ); + + EditorSettings.setValue( "libraryTab", DatablockEditorTreeTabBook.getSelectedPage() ); + if( %this.getNumSelectedDatablocks() > 0 ) + EditorSettings.setValue( "selectedDatablock", %this.getSelectedDatablock().getName() ); + + EditorSettings.endGroup(); +} + +//============================================================================================= +// Persistence. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +/// Return true if there is any datablock with unsaved changes. +function DatablockEditorPlugin::isDirty( %this ) +{ + return %this.PM.hasDirty(); +} + +//--------------------------------------------------------------------------------------------- + +/// Return true if any of the currently selected datablocks has unsaved changes. +function DatablockEditorPlugin::selectedDatablockIsDirty( %this ) +{ + %tree = DatablockEditorTree; + + %count = %tree.getSelectedItemsCount(); + %selected = %tree.getSelectedItemList(); + + foreach$( %id in %selected ) + { + %db = %tree.getItemValue( %id ); + if( %this.PM.isDirty( %db ) ) + return true; + } + + return false; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::syncDirtyState( %this ) +{ + %tree = DatablockEditorTree; + + %count = %tree.getSelectedItemsCount(); + %selected = %tree.getSelectedItemList(); + %haveDirty = false; + + foreach$( %id in %selected ) + { + %db = %tree.getItemValue( %id ); + if( %this.PM.isDirty( %db ) ) + { + %this.flagDatablockAsDirty( %db, true ); + %haveDirty = true; + } + else + %this.flagInspectorAsDirty( %db, false ); + } + + %this.flagInspectorAsDirty( %haveDirty ); +} + +//--------------------------------------------------------------------------------------------- + +/// +function DatablockEditorPlugin::flagInspectorAsDirty( %this, %dirty ) +{ + if( %dirty ) + DatablockEditorInspectorWindow.text = "Datablock *"; + else + DatablockEditorInspectorWindow.text = "Datablock"; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::flagDatablockAsDirty(%this, %datablock, %dirty ) +{ + %tree = DatablockEditorTree; + + %id = %tree.findItemByValue( %datablock.getId() ); + if( %id == 0 ) + return; + + // Tag the item caption and sync the persistence manager. + + if( %dirty ) + { + DatablockEditorTree.editItem( %id, %datablock.getName() @ " *", %datablock.getId() ); + %this.PM.setDirty( %datablock ); + } + else + { + DatablockEditorTree.editItem( %id, %datablock.getName(), %datablock.getId() ); + %this.PM.removeDirty( %datablock ); + } + + // Sync the inspector dirty state. + + %this.flagInspectorAsDirty( %this.PM.hasDirty() ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::showSaveNewFileDialog(%this) +{ + %currentFile = %this.getSelectedDatablock().getFilename(); + getSaveFilename( "TorqueScript Files|*.cs|All Files|*.*", %this @ ".saveNewFileFinish", %currentFile, false ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::saveNewFileFinish( %this, %newFileName ) +{ + // Clear the first responder to capture any inspector changes + %ctrl = canvas.getFirstResponder(); + if( isObject(%ctrl) ) + %ctrl.clearFirstResponder(); + + %tree = DatablockEditorTree; + %count = %tree.getSelectedItemsCount(); + %selected = %tree.getSelectedItemList(); + + foreach$( %id in %selected ) + { + %db = %tree.getItemValue( %id ); + %db = %this.getSelectedDatablock(); + + // Remove from current file. + + %oldFileName = %db.getFileName(); + if( %oldFileName !$= "" ) + %this.PM.removeObjectFromFile( %db, %oldFileName ); + + // Save to new file. + + %this.PM.setDirty( %db, %newFileName ); + if( %this.PM.saveDirtyObject( %db ) ) + { + // Clear dirty state. + + %this.flagDatablockAsDirty( %db, false ); + } + } + + DatablockEditorInspectorWindow-->DatablockFile.setText( %newFileName ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::save( %this ) +{ + // Clear the first responder to capture any inspector changes + %ctrl = canvas.getFirstResponder(); + if( isObject(%ctrl) ) + %ctrl.clearFirstResponder(); + + %tree = DatablockEditorTree; + %count = %tree.getSelectedItemsCount(); + %selected = %tree.getSelectedItemList(); + + for( %i = 0; %i < %count; %i ++ ) + { + %id = getWord( %selected, %i ); + %db = %tree.getItemValue( %id ); + + if( %this.PM.isDirty( %db ) ) + { + %this.PM.saveDirtyObject( %db ); + %this.flagDatablockAsDirty( %db, false ); + } + } +} + +//============================================================================================= +// Selection. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::getNumSelectedDatablocks( %this ) +{ + return DatablockEditorTree.getSelectedItemsCount(); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::getSelectedDatablock( %this, %index ) +{ + %tree = DatablockEditorTree; + if( !%tree.getSelectedItemsCount() ) + return 0; + + if( !%index ) + %id = %tree.getSelectedItem(); + else + %id = getWord( %tree.getSelectedItemList(), %index ); + + return %tree.getItemValue( %id ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::resetSelectedDatablock( %this ) +{ + DatablockEditorTree.clearSelection(); + DatablockEditorInspector.inspect(0); + DatablockEditorInspectorWindow-->DatablockFile.setText(""); + + EditorGuiStatusBar.setSelection( "" ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::selectDatablockCheck( %this, %datablock ) +{ + if( %this.selectedDatablockIsDirty() ) + %this.showSaveDialog( %datablock ); + else + %this.selectDatablock( %datablock ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::selectDatablock( %this, %datablock, %add, %dontSyncTree ) +{ + if( %add ) + DatablockEditorInspector.addInspect( %datablock ); + else + DatablockEditorInspector.inspect( %datablock ); + + if( !%dontSyncTree ) + { + %id = DatablockEditorTree.findItemByValue( %datablock.getId() ); + + if( !%add ) + DatablockEditorTree.clearSelection(); + + DatablockEditorTree.selectItem( %id, true ); + DatablockEditorTree.scrollVisible( %id ); + } + + %this.syncDirtyState(); + + // Update the filename text field. + + %numSelected = %this.getNumSelectedDatablocks(); + %fileNameField = DatablockEditorInspectorWindow-->DatablockFile; + + if( %numSelected == 1 ) + { + %fileName = %datablock.getFilename(); + if( %fileName !$= "" ) + %fileNameField.setText( %fileName ); + else + %fileNameField.setText( $DATABLOCK_EDITOR_DEFAULT_FILENAME ); + } + else + { + %fileNameField.setText( "" ); + } + + EditorGuiStatusBar.setSelection( %this.getNumSelectedDatablocks() @ " Datablocks Selected" ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::unselectDatablock( %this, %datablock, %dontSyncTree ) +{ + DatablockEditorInspector.removeInspect( %datablock ); + + if( !%dontSyncTree ) + { + %id = DatablockEditorTree.findItemByValue( %datablock.getId() ); + DatablockEditorTree.selectItem( %id, false ); + } + + %this.syncDirtyState(); + + // If we have exactly one selected datablock remaining, re-enable + // the save-as button. + + %numSelected = %this.getNumSelectedDatablocks(); + if( %numSelected == 1 ) + { + DatablockEditorInspectorWindow-->saveAsButton.setActive( true ); + + %fileNameField = DatablockEditorInspectorWindow-->DatablockFile; + %fileNameField.setText( %this.getSelectedDatablock().getFilename() ); + %fileNameField.setActive( true ); + } + + EditorGuiStatusBar.setSelection( %this.getNumSelectedDatablocks() @ " Datablocks Selected" ); +} + +//============================================================================================= +// Creation and Deletion. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::deleteDatablock( %this ) +{ + %tree = DatablockEditorTree; + + // If we have more than single datablock selected, + // turn our undos into a compound undo. + + %numSelected = %tree.getSelectedItemsCount(); + if( %numSelected > 1 ) + Editor.getUndoManager().pushCompound( "Delete Multiple Datablocks" ); + + for( %i = 0; %i < %numSelected; %i ++ ) + { + %id = %tree.getSelectedItem( %i ); + %db = %tree.getItemValue( %id ); + + %fileName = %db.getFileName(); + + // Remove the datablock from the tree. + + DatablockEditorTree.removeItem( %id ); + + // Create undo. + + %action = %this.createUndo( ActionDeleteDatablock, "Delete Datablock" ); + %action.db = %db; + %action.dbName = %db.getName(); + %action.fname = %fileName; + + %this.submitUndo( %action ); + + // Kill the datablock in the file. + + if( %fileName !$= "" ) + %this.PM.removeObjectFromFile( %db ); + + UnlistedDatablocks.add( %db ); + + // Show some confirmation. + + if( %numSelected == 1 ) + MessageBoxOk( "Datablock Deleted", "The datablock (" @ %db.getName() @ ") has been removed from " @ + "it's file (" @ %db.getFilename() @ ") and upon restart will cease to exist" ); + } + + // Close compound, if we were deleting multiple datablocks. + + if( %numSelected > 1 ) + Editor.getUndoManager().popCompound(); + + // Show confirmation for multiple datablocks. + + if( %numSelected > 1 ) + MessageBoxOk( "Datablocks Deleted", "The datablocks have been deleted and upon restart will cease to exist." ); + + // Clear selection. + + DatablockEditorPlugin.resetSelectedDatablock(); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::createDatablock(%this) +{ + %class = DatablockEditorTypeTree.getItemText(DatablockEditorTypeTree.getSelectedItem()); + if( %class !$= "" ) + { + // Need to prompt for a name. + + DatablockEditorCreatePrompt-->CreateDatablockName.setText("Name"); + DatablockEditorCreatePrompt-->CreateDatablockName.selectAllText(); + + // Populate the copy source dropdown. + + %list = DatablockEditorCreatePrompt-->CopySourceDropdown; + %list.clear(); + %list.add( "", 0 ); + + %set = DataBlockSet; + %count = %set.getCount(); + for( %i = 0; %i < %count; %i ++ ) + { + %datablock = %set.getObject( %i ); + %datablockClass = %datablock.getClassName(); + + if( !isMemberOfClass( %datablockClass, %class ) ) + continue; + + %list.add( %datablock.getName(), %i + 1 ); + } + + // Set up state of client-side checkbox. + + %clientSideCheckBox = DatablockEditorCreatePrompt-->ClientSideCheckBox; + %canBeClientSide = DatablockEditorPlugin::canBeClientSideDatablock( %class ); + %clientSideCheckBox.setStateOn( %canBeClientSide ); + %clientSideCheckBox.setActive( %canBeClientSide ); + + // Show the dialog. + + canvas.pushDialog( DatablockEditorCreatePrompt, 0, true ); + } +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::createPromptNameCheck(%this) +{ + %name = DatablockEditorCreatePrompt-->CreateDatablockName.getText(); + if( !Editor::validateObjectName( %name, true ) ) + return; + + // Fetch the copy source and clear the list. + + %copySource = DatablockEditorCreatePrompt-->copySourceDropdown.getText(); + DatablockEditorCreatePrompt-->copySourceDropdown.clear(); + + // Remove the dialog and create the datablock. + + canvas.popDialog( DatablockEditorCreatePrompt ); + %this.createDatablockFinish( %name, %copySource ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::createDatablockFinish( %this, %name, %copySource ) +{ + %class = DatablockEditorTypeTree.getItemText(DatablockEditorTypeTree.getSelectedItem()); + if( %class !$= "" ) + { + %action = %this.createUndo( ActionCreateDatablock, "Create New Datablock" ); + + if( DatablockEditorCreatePrompt-->ClientSideCheckBox.isStateOn() ) + %dbType = "singleton "; + else + %dbType = "datablock "; + + if( %copySource !$= "" ) + %eval = %dbType @ %class @ "(" @ %name @ " : " @ %copySource @ ") { canSaveDynamicFields = \"1\"; };"; + else + %eval = %dbType @ %class @ "(" @ %name @ ") { canSaveDynamicFields = \"1\"; };"; + + %res = eval( %eval ); + + %action.db = %name.getId(); + %action.dbName = %name; + %action.fname = $DATABLOCK_EDITOR_DEFAULT_FILENAME; + + %this.submitUndo( %action ); + + %action.redo(); + } +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::canBeClientSideDatablock( %className ) +{ + switch$( %className ) + { + case "SFXProfile" or + "SFXPlayList" or + "SFXAmbience" or + "SFXEnvironment" or + "SFXState" or + "SFXDescription" or + "SFXFMODProject": + return true; + + default: + return false; + } +} + +//============================================================================================= +// Events. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); + + DatablockEditorPlugin.flagDatablockAsDirty( %object, true ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + DatablockFieldInfoControl.setText( "" @ %fieldName @ " (" @ %fieldTypeStr @ ") " NL "" @ %fieldDoc ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorInspector::onBeginCompoundEdit( %this ) +{ + Editor.getUndoManager().pushCompound( "Multiple Field Edit" ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorInspector::onEndCompoundEdit( %this, %discard ) +{ + Editor.getUndoManager().popCompound( %discard ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorInspector::onClear( %this ) +{ + DatablockFieldInfoControl.setText( "" ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onDeleteSelection( %this ) +{ + %this.undoDeleteList = ""; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onDeleteObject( %this, %object ) +{ + // Append it to our list. + %this.undoDeleteList = %this.undoDeleteList TAB %object; + + // We're gonna delete this ourselves in the + // completion callback. + return true; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onObjectDeleteCompleted( %this ) +{ + //MEDeleteUndoAction::submit( %this.undoDeleteList ); + + // Let the world editor know to + // clear its selection. + //EWorldEditor.clearSelection(); + //EWorldEditor.isDirty = true; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onClearSelected(%this) +{ + DatablockEditorInspector.inspect( 0 ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onAddSelection( %this, %id ) +{ + %obj = %this.getItemValue( %id ); + + if( !isObject( %obj ) ) + %this.selectItem( %id, false ); + else + DatablockEditorPlugin.selectDatablock( %obj, true, true ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onRemoveSelection( %this, %id ) +{ + %obj = %this.getItemValue( %id ); + if( isObject( %obj ) ) + DatablockEditorPlugin.unselectDatablock( %obj, true ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTree::onRightMouseUp( %this, %id, %mousePos ) +{ + %datablock = %this.getItemValue( %id ); + if( !isObject( %datablock ) ) + return; + + if( !isObject( DatablockEditorTreePopup ) ) + new PopupMenu( DatablockEditorTreePopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Delete" TAB "" TAB "DatablockEditorPlugin.selectDatablock( %this.datablockObject ); DatablockEditorPlugin.deleteDatablock( %this.datablockObject );"; + item[ 1 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenDeclarationInTorsion( %this.datablockObject );"; + + datablockObject = ""; + }; + + DatablockEditorTreePopup.datablockObject = %datablock; + DatablockEditorTreePopup.showPopup( Canvas ); +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorTreeTabBook::onTabSelected(%this, %text, %id) +{ + switch(%id) + { + case 0: + DatablockEditorTreeWindow-->DeleteSelection.visible = true; + DatablockEditorTreeWindow-->CreateSelection.visible = false; + + case 1: + DatablockEditorTreeWindow-->DeleteSelection.visible = false; + DatablockEditorTreeWindow-->CreateSelection.visible = true; + } +} diff --git a/Templates/BaseGame/game/tools/datablockEditor/datablockEditorUndo.cs b/Templates/BaseGame/game/tools/datablockEditor/datablockEditorUndo.cs new file mode 100644 index 000000000..f9a8d30b4 --- /dev/null +++ b/Templates/BaseGame/game/tools/datablockEditor/datablockEditorUndo.cs @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::createUndo( %this, %class, %desc ) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = %class; + superClass = BaseDatablockEdAction; + actionName = %desc; + editor = DatablockEditorPlugin; + treeview = DatablockEditorTree; + inspector = DatablockEditorInspector; + }; + popInstantGroup(); + return %action; +} + +//--------------------------------------------------------------------------------------------- + +function DatablockEditorPlugin::submitUndo( %this, %action ) +{ + %action.addToManager( Editor.getUndoManager() ); +} + +//============================================================================================= +// BaseDatablockEdAction. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function BaseDatablockEdAction::redo( %this ) +{ +} + +//--------------------------------------------------------------------------------------------- + +function BaseDatablockEdAction::undo( %this ) +{ +} + +//============================================================================================= +// ActionCreateDatablock. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionCreateDatablock::redo( %this ) +{ + %db = %this.db; + + %db.name = %this.dbName; + + %this.editor.PM.setDirty( %db, %this.fname ); + %this.editor.addExistingItem( %db ); + %this.editor.selectDatablock( %db ); + %this.editor.flagInspectorAsDirty( true ); + + UnlistedDatablocks.remove( %id ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionCreateDatablock::undo( %this ) +{ + %db = %this.db; + + %itemId = %this.treeview.findItemByName( %db.name ); + if( !%itemId ) + %itemId = %this.treeview.findItemByName( %db.name @ " *" ); + + %this.treeview.removeItem( %itemId ); + %this.editor.resetSelectedDatablock(); + %this.editor.PM.removeDirty( %db ); + + %this.dbName = %db.name; + %db.name = ""; + + UnlistedDatablocks.add( %this.db ); +} + +//============================================================================================= +// ActionDeleteDatablock. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteDatablock::redo( %this ) +{ + %db = %this.db; + + %itemId = %this.treeview.findItemByName( %db.name ); + if( !%itemId ) + %itemId = %this.treeview.findItemByName( %db.name @ " *" ); + + // Remove from tree and file. + + %this.treeview.removeItem( %db ); + %this.editor.resetSelectedDatablock(); + if( %db.getFileName() !$= "" ) + %this.editor.PM.removeObjectFromFile( %db ); + + // Unassign name. + + %this.dbName = %db.name; + %db.name = ""; + + // Add to unlisted. + + UnlistedDatablocks.add( %db ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteDatablock::undo( %this ) +{ + %db = %this.db; + + // Restore name. + + %db.name = %this.dbName; + + // Add to tree and select. + + %this.editor.addExistingItem( %db, true ); + %this.editor.selectDatablock( %db ); + + // Mark as dirty. + + %this.editor.PM.setDirty( %db, %this.fname ); + %this.editor.syncDirtyState(); + + // Remove from unlisted. + + UnlistedDatablocks.remove( %id ); +} diff --git a/Templates/BaseGame/game/tools/datablockEditor/main.cs b/Templates/BaseGame/game/tools/datablockEditor/main.cs new file mode 100644 index 000000000..e7626a8b3 --- /dev/null +++ b/Templates/BaseGame/game/tools/datablockEditor/main.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------------------------- + +function initializeDatablockEditor() +{ + echo( " - Initializing Datablock Editor" ); + + exec("./datablockEditor.cs"); + exec("./datablockEditorUndo.cs"); + exec("./DatablockEditorTreeWindow.ed.gui"); + exec("./DatablockEditorInspectorWindow.ed.gui"); + exec("./DatablockEditorCreatePrompt.ed.gui"); + + // Add ourselves to EditorGui, where all the other tools reside + DatablockEditorInspectorWindow.setVisible( false ); + DatablockEditorTreeWindow.setVisible( false ); + + EditorGui.add( DatablockEditorInspectorWindow ); + EditorGui.add( DatablockEditorTreeWindow ); + + new ScriptObject( DatablockEditorPlugin ) + { + superClass = "WorldEditorPlugin"; + editorGui = EWorldEditor; + }; + + new SimSet( UnlistedDatablocks ); + + // create our persistence manager + DatablockEditorPlugin.PM = new PersistenceManager(); + + %map = new ActionMap(); + %map.bindCmd( keyboard, "backspace", "DatablockEditorPlugin.onDeleteKey();", "" ); + %map.bindCmd( keyboard, "delete", "DatablockEditorPlugin.onDeleteKey();", "" ); + DatablockEditorPlugin.map = %map; + + DatablockEditorPlugin.initSettings(); +} + +//--------------------------------------------------------------------------------------------- + +function destroyDatablockEditor() +{ +} diff --git a/Templates/BaseGame/game/tools/debugger/gui/breakConditionDlg.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/breakConditionDlg.ed.gui new file mode 100644 index 000000000..d6355e58f --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/breakConditionDlg.ed.gui @@ -0,0 +1,145 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerBreakConditionDlg, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "220 146"; + extent = "200 188"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Set the break condition"; + resizeWidth = "True"; + resizeHeight = "True"; + canMove = "False"; + canClose = "False"; + canMinimize = "False"; + canMaximize = "False"; + minSize = "50 50"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 28"; + extent = "121 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Enter the break condition:"; + }; + new GuiTextEditCtrl(BreakCondition) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 44"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + altCommand = "DbgBreakConditionSet();"; + helpTag = "0"; + historySize = "0"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 68"; + extent = "57 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Pass Count:"; + }; + new GuiTextEditCtrl(BreakPassCount) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 84"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + historySize = "0"; + returnTab = "true"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 108"; + extent = "27 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Clear:"; + }; + new GuiTextEditCtrl(BreakClear) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 124"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + historySize = "0"; + returnTab = "true"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "56 156"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgBreakConditionSet();"; + helpTag = "0"; + text = "Set"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "104 156"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.popDialog(DebuggerBreakConditionDlg);"; + helpTag = "0"; + text = "Cancel"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/gui/connectDlg.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/connectDlg.ed.gui new file mode 100644 index 000000000..927312fed --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/connectDlg.ed.gui @@ -0,0 +1,148 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerConnectDlg, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "220 146"; + extent = "200 188"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Connect to server:"; + resizeWidth = "True"; + resizeHeight = "True"; + canMove = "False"; + canClose = "False"; + canMinimize = "False"; + canMaximize = "False"; + minSize = "50 50"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 28"; + extent = "55 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "IP Address:"; + }; + new GuiTextEditCtrl(DebuggerConnectAddress) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 44"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + variable = "$pref::DBGConnectAddress"; + helpTag = "0"; + historySize = "0"; + returnTab = "true"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 68"; + extent = "21 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Port:"; + }; + new GuiTextEditCtrl(DebuggerConnectPort) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 84"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + variable = "$pref::DBGConnectPort"; + helpTag = "0"; + historySize = "0"; + returnTab = "true"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 108"; + extent = "52 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Password:"; + }; + new GuiTextEditCtrl(DebuggerConnectPassword) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 124"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + variable = "$pref::DBGConnectPassword"; + helpTag = "0"; + historySize = "0"; + returnTab = "true"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "56 156"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgConnect();"; + helpTag = "0"; + text = "Open"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "104 156"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.popDialog(DebuggerConnectDlg);"; + helpTag = "0"; + text = "Cancel"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/gui/debugger.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/debugger.ed.gui new file mode 100644 index 000000000..616a8f0fb --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/debugger.ed.gui @@ -0,0 +1,583 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerGui, EditorGuiGroup) { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "8 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(DebuggerConnectDlg, 80);"; + helpTag = "0"; + text = "Connect"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "72 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(OpenFileDialog, 80);"; + helpTag = "0"; + text = "File"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "72 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "dbgStepIn();"; + accelerator = "f7"; + helpTag = "0"; + text = "Step In"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "136 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "dbgStepOver();"; + accelerator = "f8"; + helpTag = "0"; + text = "Step Over"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "200 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "dbgStepOut();"; + accelerator = "f6"; + helpTag = "0"; + text = "Step Out"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "264 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "dbgContinue();"; + accelerator = "f9"; + helpTag = "0"; + text = "Run"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "328 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(DebuggerFindDlg, 80);"; + helpTag = "0"; + text = "Find"; + }; + new GuiTextCtrl(DebuggerCursorWatch) { + profile = "ToolsGuiTextProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "398 4"; + extent = "126 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = ""; + justify = "left"; + }; + new GuiTextCtrl(DebuggerStatus) { + profile = "ToolsGuiTextProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "532 4"; + extent = "100 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "NOT CONNECTED"; + justify = "right"; + }; + new GuiFrameSetCtrl(DebuggerRootFrame) { + profile = "GuiContentProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 24"; + extent = "640 456"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + columns = "0 486"; + rows = "0"; + borderWidth = "4"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + + new GuiFrameSetCtrl(DebuggerLeftFrame) { + profile = "GuiContentProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "482 456"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + columns = "0"; + rows = "0 350"; + borderWidth = "4"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + + new GuiControl() { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "482 346"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "True"; + modal = "True"; + helpTag = "0"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "8 4"; + extent = "47 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Open File:"; + }; + new GuiPopUpMenuCtrl(DebuggerFilePopup) { + profile = "ToolsGuiPopUpMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "64 4"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "True"; + modal = "True"; + helpTag = "0"; + maxPopupHeight = "200"; + }; + new GuiScrollCtrl() { + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 24"; + extent = "482 321"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + willFirstRespond = "True"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "False"; + + new DbgFileView(DebuggerFileView) { + profile = "ToolsGuiTextArrayProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 -433"; + extent = "509 3904"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + }; + }; + }; + new GuiControl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 350"; + extent = "482 106"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "8 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(DebuggerWatchDlg, 80);"; + helpTag = "0"; + text = "Add"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "72 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(DebuggerEditWatchDlg, 80);"; + helpTag = "0"; + text = "Edit"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "136 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgDeleteSelectedWatch();"; + helpTag = "0"; + text = "Delete"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "200 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DebuggerWatchView.clear();"; + helpTag = "0"; + text = "Clear"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "264 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgRefreshWatches();"; + helpTag = "0"; + text = "Refresh"; + }; + new GuiScrollCtrl() { + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 24"; + extent = "481 80"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + willFirstRespond = "True"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "False"; + + new GuiTextListCtrl(DebuggerWatchView) { + profile = "ToolsGuiTextListProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 8"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + enumerate = "False"; + resizeCell = "True"; + columns = "0 200"; + }; + }; + }; + }; + new GuiFrameSetCtrl(DebuggerRightFrame) { + profile = "GuiContentProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "486 0"; + extent = "154 456"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + columns = "0"; + rows = "0 150 350"; + borderWidth = "4"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + + new GuiScrollCtrl() { + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "154 146"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + willFirstRespond = "True"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "False"; + + new GuiTextListCtrl(DebuggerCallStack) { + profile = "ToolsGuiTextListProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 8"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + enumerate = "False"; + resizeCell = "True"; + columns = "-1 -1 0"; + }; + }; + new GuiControl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 150"; + extent = "154 196"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "4 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.pushDialog(DebuggerBreakConditionDlg, 80);"; + helpTag = "0"; + text = "Condition"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "68 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgDeleteSelectedBreak();"; + helpTag = "0"; + text = "Delete"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "132 4"; + extent = "56 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DebuggerBreakPoints.clearBreaks();"; + helpTag = "0"; + text = "Clear"; + }; + new GuiScrollCtrl() { + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 24"; + extent = "153 171"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + willFirstRespond = "True"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "False"; + + new GuiTextListCtrl(DebuggerBreakPoints) { + profile = "ToolsGuiTextListProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "182 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + enumerate = "False"; + resizeCell = "True"; + columns = "16 56 156"; + }; + }; + }; + new GuiControl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 350"; + extent = "154 106"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiScrollCtrl() { + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "153 80"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + willFirstRespond = "True"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "False"; + + new GuiTextListCtrl(DebuggerConsoleView) { + profile = "ToolsGuiTextListProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "62 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + enumerate = "False"; + resizeCell = "True"; + columns = "0"; + }; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "15 83"; + extent = "9 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "%"; + }; + new GuiTextEditCtrl(DbgConsoleEntry) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "top"; + position = "29 83"; + extent = "120 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + altCommand = "DbgConsoleEntryReturn();"; + helpTag = "0"; + historySize = "32"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/gui/editWatchDlg.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/editWatchDlg.ed.gui new file mode 100644 index 000000000..d1d5ebe4e --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/editWatchDlg.ed.gui @@ -0,0 +1,93 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerEditWatchDlg, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "220 180"; + extent = "200 108"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Edit a Variable"; + resizeWidth = "True"; + resizeHeight = "True"; + canMove = "False"; + canClose = "False"; + canMinimize = "False"; + canMaximize = "False"; + minSize = "50 50"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 28"; + extent = "99 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Enter the new value:"; + }; + new GuiTextEditCtrl(EditWatchDialogValue) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 44"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + altCommand = "DbgWatchDialogEdit();"; + helpTag = "0"; + historySize = "0"; + fontHL = "14 253 Arial"; + font = "14 244 Arial"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "56 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgWatchDialogEdit();"; + helpTag = "0"; + text = "Edit"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "104 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.popDialog(DebuggerEditWatchDlg);"; + helpTag = "0"; + text = "Cancel"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/gui/findDlg.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/findDlg.ed.gui new file mode 100644 index 000000000..5a74273d5 --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/findDlg.ed.gui @@ -0,0 +1,93 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerFindDlg, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "220 180"; + extent = "200 108"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "File Search"; + resizeWidth = "True"; + resizeHeight = "True"; + canMove = "False"; + canClose = "False"; + canMinimize = "False"; + canMaximize = "False"; + minSize = "50 50"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 28"; + extent = "99 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Search for:"; + }; + new GuiTextEditCtrl(DebuggerFindStringText) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 44"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + altCommand = "DbgFileViewFind();"; + helpTag = "0"; + historySize = "0"; + fontHL = "14 253 Arial"; + font = "14 244 Arial"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "56 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgFileViewFind();"; + helpTag = "0"; + text = "Find"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "104 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.popDialog(DebuggerFindDlg);"; + helpTag = "0"; + text = "Cancel"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/gui/watchDlg.ed.gui b/Templates/BaseGame/game/tools/debugger/gui/watchDlg.ed.gui new file mode 100644 index 000000000..898563807 --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/gui/watchDlg.ed.gui @@ -0,0 +1,92 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(DebuggerWatchDlg, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "220 180"; + extent = "200 108"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Add a Watch Expression:"; + resizeWidth = "True"; + resizeHeight = "True"; + canMove = "False"; + canClose = "False"; + canMinimize = "False"; + canMaximize = "False"; + minSize = "50 50"; + opaque = "true"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 28"; + extent = "88 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + helpTag = "0"; + text = "Enter the Variable:"; + }; + new GuiTextEditCtrl(WatchDialogExpression) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 44"; + extent = "160 18"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + altCommand = "DbgWatchDialogAdd();"; + helpTag = "0"; + historySize = "0"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "56 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "DbgWatchDialogAdd();"; + helpTag = "0"; + text = "Add"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "104 80"; + extent = "40 16"; + minExtent = "8 8"; + visible = "True"; + setFirstResponder = "False"; + modal = "True"; + command = "Canvas.popDialog(DebuggerWatchDlg);"; + helpTag = "0"; + text = "Cancel"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/debugger/main.cs b/Templates/BaseGame/game/tools/debugger/main.cs new file mode 100644 index 000000000..f51222665 --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/main.cs @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// TCP Debugger +// To use the debugger, first call "dbgSetParameters(port, password);" from one instance of +// your game. Then, in another instance (either on the same system, or a different one) call +// "startDebugger();". Then use the gui to connect to the first instance with the port and +// password you first passed to dbgSetParameters. +//--------------------------------------------------------------------------------------------- + +function initializeDebugger() +{ + echo(" % - Initializing Debugger"); + + // Load the scripts. + exec("./scripts/debugger.ed.cs"); + + // And the guis. + exec("./gui/breakConditionDlg.ed.gui"); + exec("./gui/connectDlg.ed.gui"); + exec("./gui/editWatchDlg.ed.gui"); + exec("./gui/findDlg.ed.gui"); + exec("./gui/debugger.ed.gui"); + exec("./gui/watchDlg.ed.gui"); +} + +function destroyDebugger() +{ + if (isObject(TCPDebugger)) + TCPDebugger.delete(); +} + +function startDebugger() +{ + // Clean up first. + destroyDebugger(); + + // Create a TCP object named TCPDebugger. + new TCPObject(TCPDebugger); + + // Used to get unique IDs for breakpoints and watch expressions. + $DbgBreakId = 0; + $DbgWatchSeq = 1; + + // Set up the GUI. + DebuggerConsoleView.setActive(false); + $GameCanvas.pushDialog(DebuggerGui); +} diff --git a/Templates/BaseGame/game/tools/debugger/scripts/debugger.ed.cs b/Templates/BaseGame/game/tools/debugger/scripts/debugger.ed.cs new file mode 100644 index 000000000..408de29eb --- /dev/null +++ b/Templates/BaseGame/game/tools/debugger/scripts/debugger.ed.cs @@ -0,0 +1,508 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// onLine is invoked whenever the TCP object receives a line from the server. Treat the first +// word as a "command" and dispatch to an appropriate handler. +//--------------------------------------------------------------------------------------------- +function TCPDebugger::onLine(%this, %line) +{ + echo("Got line=>" @ %line); + %cmd = firstWord(%line); + %rest = restWords(%line); + + if (%cmd $= "PASS") { + %this.handlePass(%rest); + } + else if(%cmd $= "COUT") { + %this.handleLineOut(%rest); + } + else if(%cmd $= "FILELISTOUT") { + %this.handleFileList(%rest); + } + else if(%cmd $= "BREAKLISTOUT") { + %this.handleBreakList(%rest); + } + else if(%cmd $= "BREAK") { + %this.handleBreak(%rest); + } + else if(%cmd $= "RUNNING") { + %this.handleRunning(); + } + else if(%cmd $= "EVALOUT") { + %this.handleEvalOut(%rest); + } + else { + %this.handleError(%line); + } +} + +// Handler for PASS response. +function TCPDebugger::handlePass(%this, %message) +{ + if (%message $= "WrongPass") { + DebuggerConsoleView.print("Disconnected - wrong password."); + %this.disconnect(); + } + else if(%message $= "Connected.") { + DebuggerConsoleView.print("Connected."); + DebuggerStatus.setValue("CONNECTED"); + %this.send("FILELIST\r\n"); + } +} + +// Handler for COUT response. +function TCPDebugger::handleLineOut(%this, %line) +{ + DebuggerConsoleView.print(%line); +} + +// Handler for FILELISTOUT response. +function TCPDebugger::handleFileList(%this, %line) +{ + DebuggerFilePopup.clear(); + %word = 0; + while ((%file = getWord(%line, %word)) !$= "") { + %word++; + DebuggerFilePopup.add(%file, %word); + } +} + +// Handler for BREAKLISTOUT response. +function TCPDebugger::handleBreakList(%this, %line) +{ + %file = getWord(%line, 0); + if (%file != $DebuggerFile) { + return; + } + %pairs = getWord(%line, 1); + %curLine = 1; + DebuggerFileView.clearBreakPositions(); + + // Set the possible break positions. + for (%i = 0; %i < %pairs; %i++) { + %skip = getWord(%line, %i * 2 + 2); + %breaks = getWord(%line, %i * 2 + 3); + %curLine += %skip; + for (%j = 0; %j < %breaks; %j++) { + DebuggerFileView.setBreakPosition(%curLine); + %curLine++; + } + } + + // Now set the actual break points. + for (%i = 0; %i < DebuggerBreakPoints.rowCount(); %i++) { + %breakText = DebuggerBreakPoints.getRowText(%i); + %breakLine = getField(%breakText, 0); + %breakFile = getField(%breakText, 1); + if (%breakFile == $DebuggerFile) { + DebuggerFileView.setBreak(%breakLine); + } + } +} + +// Handler for BREAK response. +function TCPDebugger::handleBreak(%this, %line) +{ + DebuggerStatus.setValue("BREAK"); + + // Query all the watches. + for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) { + %id = DebuggerWatchView.getRowId(%i); + %row = DebuggerWatchView.getRowTextById(%id); + %expr = getField(%row, 0); + %this.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n"); + } + + // Update the call stack window. + DebuggerCallStack.clear(); + + %file = getWord(%line, 0); + %lineNumber = getWord(%line, 1); + %funcName = getWord(%line, 2); + + DbgOpenFile(%file, %lineNumber, true); + + %nextWord = 3; + %rowId = 0; + %id = 0; + while(1) { + DebuggerCallStack.setRowById(%id, %file @ "\t" @ %lineNumber @ "\t" @ %funcName); + %id++; + %file = getWord(%line, %nextWord); + %lineNumber = getWord(%line, %nextWord + 1); + %funcName = getWord(%line, %nextWord + 2); + %nextWord += 3; + if (%file $= "") { + break; + } + } +} + +// Handler for RUNNING response. +function TCPDebugger::handleRunning(%this) +{ + DebuggerFileView.setCurrentLine(-1, true); + DebuggerCallStack.clear(); + DebuggerStatus.setValue("RUNNING..."); +} + +// Handler for EVALOUT response. +function TCPDebugger::handleEvalOut(%this, %line) +{ + %id = firstWord(%line); + %value = restWords(%line); + + // See if it's the cursor watch, or from the watch window. + if (%id < 0) { + DebuggerCursorWatch.setText(DebuggerCursorWatch.expr SPC "=" SPC %value); + } + else { + %row = DebuggerWatchView.getRowTextById(%id); + if (%row $= "") { + return; + } + %expr = getField(%row, 0); + DebuggerWatchView.setRowById(%id, %expr @ "\t" @ %value); + } +} + +// Handler for unrecognized response. +function TCPDebugger::handleError(%this, %line) +{ + DebuggerConsoleView.print("ERROR - bogus message: " @ %line); +} + +// Print a line of response from the server. +function DebuggerConsoleView::print(%this, %line) +{ + %row = %this.addRow(0, %line); + %this.scrollVisible(%row); +} + +// When entry in file list selected, open the file. +function DebuggerFilePopup::onSelect(%this, %id, %text) +{ + DbgOpenFile(%text, 0, false); +} + +// When entry on call stack selected, open the file and go to the line. +function DebuggerCallStack::onAction(%this) +{ + %id = %this.getSelectedId(); + if (%id == -1) { + return; + } + %text = %this.getRowTextById(%id); + %file = getField(%text, 0); + %line = getField(%text, 1); + + DbgOpenFile(%file, %line, %id == 0); +} + +// Add a breakpoint at the selected spot, if it doesn't already exist. +function DebuggerBreakPoints::addBreak(%this, %file, %line, %clear, %passct, %expr) +{ + // columns 0 = line, 1 = file, 2 = expr + %textLine = %line @ "\t" @ %file @ "\t" @ %expr @ "\t" @ %passct @ "\t" @ %clear; + %selId = %this.getSelectedId(); + %selText = %this.getRowTextById(%selId); + if ((getField(%selText, 0) $= %line) && (getField(%selText, 1) $= %file)) { + %this.setRowById(%selId, %textLine); + } + else { + %this.addRow($DbgBreakId, %textLine); + $DbgBreakId++; + } +} + +// Remove the selected breakpoint. +function DebuggerBreakPoints::removeBreak(%this, %file, %line) +{ + for (%i = 0; %i < %this.rowCount(); %i++) { + %id = %this.getRowId(%i); + %text = %this.getRowTextById(%id); + if ((getField(%text, 0) $= %line) && (getField(%text, 1) $= %file)) { + %this.removeRowById(%id); + return; + } + } +} + +// Remove all breakpoints. +function DebuggerBreakPoints::clearBreaks(%this) +{ + while (%this.rowCount()) { + %id = %this.getRowId(0); + %text = %this.getRowTextById(%id); + %file = getField(%text, 1); + %line = getField(%text, 0); + DbgRemoveBreakPoint(%file, %line); + } +} + +// Go to file & line for the selected breakpoint. +function DebuggerBreakPoints::onAction(%this) +{ + %id = %this.getSelectedId(); + if (%id == -1) { + return; + } + %text = %this.getRowTextById(%id); + %line = getField(%text, 0); + %file = getField(%text, 1); + + DbgOpenFile(%file, %line, false); +} + +// Handle breakpoint removal executed from the file-view GUI. +function DebuggerFileView::onRemoveBreakPoint(%this, %line) +{ + %file = $DebuggerFile; + DbgRemoveBreakPoint(%file, %line); +} + +// Handle breakpoint addition executed from the file-view GUI. +function DebuggerFileView::onSetBreakPoint(%this, %line) +{ + %file = $DebuggerFile; + DbgSetBreakPoint(%file, %line, 0, 0, true); +} + +//--------------------------------------------------------------------------------------------- +// Various support functions. +//--------------------------------------------------------------------------------------------- + +// Add a watch expression. +function DbgWatchDialogAdd() +{ + %expr = WatchDialogExpression.getValue(); + if (%expr !$= "") { + DebuggerWatchView.setRowById($DbgWatchSeq, %expr @"\t(unknown)"); + TCPDebugger.send("EVAL " @ $DbgWatchSeq @ " 0 " @ %expr @ "\r\n"); + $DbgWatchSeq++; + } + Canvas.popDialog(DebuggerWatchDlg); +} + +// Edit a watch expression. +function DbgWatchDialogEdit() +{ + %newValue = EditWatchDialogValue.getValue(); + %id = DebuggerWatchView.getSelectedId(); + if (%id >= 0) { + %row = DebuggerWatchView.getRowTextById(%id); + %expr = getField(%row, 0); + if (%newValue $= "") { + %assignment = %expr @ " = \"\""; + } + else { + %assignment = %expr @ " = " @ %newValue; + } + TCPDebugger.send("EVAL " @ %id @ " 0 " @ %assignment @ "\r\n"); + } + Canvas.popDialog(DebuggerEditWatchDlg); +} + +// Set/change the singular "cursor watch" expression. +function DbgSetCursorWatch(%expr) +{ + DebuggerCursorWatch.expr = %expr; + if (DebuggerCursorWatch.expr $= "") { + DebuggerCursorWatch.setText(""); + } + else { + TCPDebugger.send("EVAL -1 0 " @ DebuggerCursorWatch.expr @ "\r\n"); + } +} + +// Connect to the server with the given addr/port/password. +function DbgConnect() +{ + %address = DebuggerConnectAddress.getValue(); + %port = DebuggerConnectPort.getValue(); + %password = DebuggerConnectPassword.getValue(); + + if ((%address !$= "" ) && (%port !$= "" ) && (%password !$= "" )) { + TCPDebugger.connect(%address @ ":" @ %port); + TCPDebugger.schedule(5000, send, %password @ "\r\n"); + TCPDebugger.password = %password; + } + + Canvas.popDialog(DebuggerConnectDlg); +} + +// Put a condition on a breakpoint. +function DbgBreakConditionSet() +{ + // Read the condition. + %condition = BreakCondition.getValue(); + %passct = BreakPassCount.getValue(); + %clear = BreakClear.getValue(); + if (%condition $= "") { + %condition = "true"; + } + if (%passct $= "") { + %passct = "0"; + } + if (%clear $= "") { + %clear = "false"; + } + + // Set the condition. + %id = DebuggerBreakPoints.getSelectedId(); + if (%id != -1) { + %bkp = DebuggerBreakPoints.getRowTextById(%id); + DbgSetBreakPoint(getField(%bkp, 1), getField(%bkp, 0), %clear, %passct, %condition); + } + + Canvas.popDialog(DebuggerBreakConditionDlg); +} + +// Open a file, go to the indicated line, and optionally select the line. +function DbgOpenFile(%file, %line, %selectLine) +{ + if (%file !$= "") { + // Open the file in the file view. + if (DebuggerFileView.open(%file)) { + // Go to the line. + DebuggerFileView.setCurrentLine(%line, %selectLine); + // Get the breakpoints for this file. + if (%file !$= $DebuggerFile) { + TCPDebugger.send("BREAKLIST " @ %file @ "\r\n"); + $DebuggerFile = %file; + } + } + } +} + +// Search in the fileview GUI. +function DbgFileViewFind() +{ + %searchString = DebuggerFindStringText.getValue(); + DebuggerFileView.findString(%searchString); + + Canvas.popDialog(DebuggerFindDlg); +} + +// Set a breakpoint, optionally with condition. +function DbgSetBreakPoint(%file, %line, %clear, %passct, %expr) +{ + if (!%clear) { + if (%file == $DebuggerFile) { + DebuggerFileView.setBreak(%line); + } + } + DebuggerBreakPoints.addBreak(%file, %line, %clear, %passct, %expr); + TCPDebugger.send("BRKSET " @ %file @ " " @ %line @ " " @ %clear @ " " @ %passct @ " " @ %expr @ "\r\n"); +} + +// Remove a breakpoint. +function DbgRemoveBreakPoint(%file, %line) +{ + if (%file == $DebuggerFile) { + DebuggerFileView.removeBreak(%line); + } + TCPDebugger.send("BRKCLR " @ %file @ " " @ %line @ "\r\n"); + DebuggerBreakPoints.removeBreak(%file, %line); +} + +// Remove whatever breakpoint is selected in the breakpoints GUI. +function DbgDeleteSelectedBreak() +{ + %selectedBreak = DebuggerBreakPoints.getSelectedId(); + %rowNum = DebuggerBreakPoints.getRowNumById(%selectedWatch); + if (%rowNum >= 0) { + %breakText = DebuggerBreakPoints.getRowText(%rowNum); + %breakLine = getField(%breakText, 0); + %breakFile = getField(%breakText, 1); + DbgRemoveBreakPoint(%breakFile, %breakLine); + } +} + +// Send an expression to the server for evaluation. +function DbgConsoleEntryReturn() +{ + %msg = DbgConsoleEntry.getValue(); + if (%msg !$= "") { + DebuggerConsoleView.print("%" @ %msg); + if (DebuggerStatus.getValue() $= "NOT CONNECTED") { + DebuggerConsoleView.print("*** Not connected."); + } + else if (DebuggerStatus.getValue() $= "BREAK") { + DebuggerConsoleView.print("*** Target is in BREAK mode."); + } + else { + TCPDebugger.send("CEVAL " @ %msg @ "\r\n"); + } + } + DbgConsoleEntry.setValue(""); +} + +// Print a line from the server. +function DbgConsolePrint(%status) +{ + DebuggerConsoleView.print(%status); +} + +// Delete the currently selected watch expression. +function DbgDeleteSelectedWatch() +{ + %selectedWatch = DebuggerWatchView.getSelectedId(); + %rowNum = DebuggerWatchView.getRowNumById(%selectedWatch); + DebuggerWatchView.removeRow(%rowNum); +} + +// Evaluate all the watch expressions. +function DbgRefreshWatches() +{ + for (%i = 0; %i < DebuggerWatchView.rowCount(); %i++) { + %id = DebuggerWatchView.getRowId(%i); + %row = DebuggerWatchView.getRowTextById(%id); + %expr = getField(%row, 0); + TCPDebugger.send("EVAL " @ %id @ " 0 " @ %expr @ "\r\n"); + } +} + +//--------------------------------------------------------------------------------------------- +// Incremental execution functions +// These just send commands to the server. +//--------------------------------------------------------------------------------------------- +function dbgStepIn() +{ + TCPDebugger.send("STEPIN\r\n"); +} + +function dbgStepOut() +{ + TCPDebugger.send("STEPOUT\r\n"); +} + +function dbgStepOver() +{ + TCPDebugger.send("STEPOVER\r\n"); +} + +function dbgContinue() +{ + TCPDebugger.send("CONTINUE\r\n"); +} diff --git a/Templates/BaseGame/game/tools/decalEditor/add-decal_d.png b/Templates/BaseGame/game/tools/decalEditor/add-decal_d.png new file mode 100644 index 0000000000000000000000000000000000000000..5e45b78ceea271bb3f3814bb62cb4fb078a6a8e7 GIT binary patch literal 880 zcmV-$1CRWPP)(V5cLLJ1UF z(KLbv5=l&?Vdqvih9F@<;zAQ+Ok5e@2k2V2Xx!??Xo7KtqMJ5xO-YPlqg81U>0>C^ z$2>jvjx|L?ACZ%s%*>s+=R5a1-#H^JEiIh`uuLAq5t$$e@OV7rGE_xPE2=szGmtJ- zi46~rAQTEg6h$1RX_^)*?d>l!AP#bNd>qGuLCCUvq(G)=BAHCacvNPaS~T=&7-HcuPG3Vsj4bCPmHcsCwcnl0V0EgSXi9L%*+)SdX2BOW0EAH z?Sf=BzhZ236oo<&m1-5C-Vk5Ybpy6-x7^=BK?HpMl7@#)Ueq+?3k9MOs1m56DCC}| zLTVf6)W~V1QfmkXyHPIxL@rlAG21=~F z#LBA|DD9Ov|C|`2CmxT(peDR~=Q`Xa2M(#uA`kb6acOE67cNe<55z!n`5g=lMqpD7 zYzoM(YilW#D-{xf30ZRcPm*Ma1hoBQoAy2L42Xd++y1`AWy~{Y&V}x}V>>Vno8J?6 z1SpakcTlV8lz>ZTUGhz9G&wm5!!WUx&0;;XkBS(8x622S0@#9xF0+I_VV6&8!!&4G zu;w-SiCJHHY4o%fgTn@*#fPHL@5k5x0qS1#oS8tNyBB))1InMiaUs+}EX!mEgI=q- z!6li$Z!T}zVHgGnVl;TYUWDu%EG>iX{wTDK_na0Mybi+pi5ah_eXzHhQ0HJhmEwSK zHAgj}AiGI?+(>Z#D@0R6UOb@#5gLIGQ~%>fqsLfX?gmW00RJ@@L4(Uy-1b-0000(RCwC7md|e!M-<23%#Q7KoF7X< zuv0m`gaV?fghT`f4ir&972?pKN)b}UAqP+oAOtN3S|qL!#;(~<41yxApQ2v1` zwvjkc(r_Sn-4wds$o8(iGsAnk-eBz7;lj#Kv!0#zKJ(sw-`K3MxR?Wgc{B??&E=ro z6H4p(TrOu#X0xA!vRbKFwr$S~0Bwy=On?l6zKj2@lv8(ZT|ZWJt9j5G#;i@RPA;5! zxH&uJZv>*)wq3TkRvM&~1MSZ38^^Zq*fDeV%$c#dx#MF)L+Kfs+sH3AVhE)a1g!u# zXMJ~7tvdNHwr(}6t_z;;!Q|#ivs5bP8}&L*CX{=Nz!#0Wu31!#jy@M>iRgkgZY zwgzwBzNIPVx~@o>rc9eDIT%JDjTr?J1|$UI;}c7T!s1RG&3e5CmC8SUCNom5x@%%s zDqvdcR4VzuTg8Nc7+m2(sw2Vr*>~Tc_+3i9wEXvyYnN>|old{pci`}2-}88*(cr-V ze~L{E9@kI~u#qv#|6$?u1IyYpIz62q{pRT0Z>jW9Gb($u(SE+s8WMw+1EsxwY0&xU zJY% zID`P%ul8HnJA_gZ&*asa(rJw+$Bzgzf{g9G1s5`4Lu_XuPze=b$quqZIM70Hl z_Y8d*&a*fkF$5iW-Sww!JS~+%pkANUc>EN>eo+91N|cTFRiI2r7_pQo1A-?+6nFhl zm0h5PAJ1L!Jm0wZ(}iE&2VD$8qM-te(@2DuO2>e&okygg>B~ hwfm>@&&fLh1^{jm5sBUhY*qjO002ovPDHLkV1nlY_ksWb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/decalEditor/add-decal_n.png b/Templates/BaseGame/game/tools/decalEditor/add-decal_n.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa1ad2c9d23465f130579b01c7d7dd91f5b2420 GIT binary patch literal 726 zcmV;{0xA88P)2W}(?R@?|~T#5byaU1*x;!5%>M8TpW_}I8fT?7kFCDBM?>Xb1J5;Do$ z8P6H-NHU~M9@+zkJNI$!ch7gfdq(g)4-UgbI3$zD4W2l@GSa_t?fMbSyeJ4ljx+E9 zzwd55ebR{n`23}IFwht)uAN)UU%YT$uh+l9+jsA^W}{KW@tgmYI?N(>dUaLb-|s-r zwjrHP>o|6nA5@r`y09Tl@U>nCMNxpjEX$%X0!X0S=mR>>+(3Bxm!;C?jZ|t$?{>Se z+uH3Q%txGx^Cd32GdB?0y`Pq)qvtqPn|piaeI7p%eZshR6jmKK<<{*xeN|QA@uP=E zkSux+{THWiZq@np7={7KWDkcJ0Tax1 z!9#aluY}zPZ(hB4Hq9LsBw5O8x`rG0o1il&m&;)HdeHCp5mfep6;lG4 z4n!c-nx;vTBh^9pE(w?bfTt`<5;YX3P@=_sg2GDAK2lIzX;1G|;K@|SH zaiWkh2V{I(4zBb2E~y+;{?wuc)`1yqZG8%t2L#ym zovII~DX39Cwc$4!3G{m7IW3B!*`{S}E!x(m2&^_4T7sc#M~HA!Q#xbTe5vKk zywA?P&k-w`nU_unF7NT+-QPXu{O|u>VRUr#D1b2hVCte-4F`bCq5umB6F>cM*>*!M1I*wI9HR;Sto;H1xr;*L&5U3B(w?*XW-3{2KdIWs> z9OZpUXEI2pQv7RP(@+qks)~CHJ=l2&k;oisYHG2u@e_$e3azdCVVWi%tj~L1-l0oZ z30OxZT|$~l91OPN%(=_173>PiR62a?4*XSBXliQ2`71Y3T^r!u$_De<3iK3^sBdlU zZK(EFZ~3&MkeRD08H}`h==At=jK6$_we>X);2opBg~MU!YiIX#r@O220p!g|+kE6-(STC`+9~S%17ulQeD;-bzqZO2)T4T$=}bE0 zC{!*9-%YT+P7$R{)q&Nu^|7g`w;@GQ=@za);w2GKfr!!=_$9yqs;*qxQjy;A00000 LNkvXXu0mjf6I8ew literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/decalEditor/decal-editor_h.png b/Templates/BaseGame/game/tools/decalEditor/decal-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f83cbe4a9c39c0ff8241a7ac3b4a727d7a32f603 GIT binary patch literal 1132 zcmV-y1e5!TP)YYiq{5n?v z6+ys!KJOpN=ci8mw^s9Owc4ssTKTiN9B3I%9ZH-^wc-n{0i=|mA3|;w8u1XSK`9Af zvFSs#0^$j+6ex*!rp8XVvwPe9`l~NaH=B)uRvI4l{jIm&IrBqq{%9~U6z367D1u_7 zYCIFZx%}nnrKP3gFPvQQZr$2~YrkDHi#T)c{P&JyE6cJb1S1sL7^zJCc)z|^c;dtf zZ@?gzvd4rK$zm=ZQuR1RrA3zJ1+2^hnPrdTjL15wygfv3E1VoW6;9(H$#UMrHavAP*yU^?PfHMwU zaHD9B&`zhbuwMGTc=Dy<4UAk^ zBZwBWP~UGjShpe7FTsc83&b;fN^}^}@}FN>fw13)ZLH7Dojs7u0d!;_7y>Zag^U$| zZQIbt??Nm+QYl7k>Yw%*m-9czW>4cbIBJlSJ{r~|Jf4@Z-{bJu@uyMAKGdt5uzT}Q z6I~1pLunF*@0JkU!v8EveX3WnEuiH*h_)Cyizqsy9klJ`Ci&`x&ALNmOxv)zSVXeVd~# z?j*_a-nsLaP)z*dg$p0ucWjwS!_U|e{l56Yho6~9(O=vwlD-G0OmxN_)GjZ5{K==i zUckdpF?3+1otLCmA(+B)8e9~Tks2XIxgIsB&N3OVPb%#7dt8PxwQpgHwPQOH&sa(G zlfLO)z=*sLFN{|1x|X(WJi(+&hjM0`Lfj@F@U5exEv;wLr;(${??VUpG#!a9z^zux y&t|hzznHb#ZFAu!^X0^0t^84^CD^0K!EK40000x2S<<0?wDV^eB}Tp z-I63pNs)Z-@b|6NCyyJyBl+;L;E!|{Tbe&TpE`H;j8QCphSzW2>g7`D5w2fDFoZL9 zc`>@=iMcsrYpVe*%YtY$YS0=!3L!LvG_t(ChEl?(Vi7b=10t@~YP3c~LGcPl*kdKd zsGg_OnXA*&Q%1AdgleVQK+23bL*181(QbdM)eJ;Z5M(wt%{#+nuhg#%_GYgz<@$}A zb~qe{hY#)_7HcGF3-P_5yrX2VdvCE=3?h*Th>Rs`BP`E+czMrXNG;&77z%|FlIOM* zMF!ipA$4h?+Es~h4i18ICymp4uU%dn&b!sgF%p`pFM?SX3Pc^YuCp2+&QOm-L73*)USV1#+ZB1o?~AC(E2UrU?%4+E-sm6&WeZFMaBe}pUWb{PL$Hy{fh;R- zq+LSBehEq%xH+t?z3**HNVe{RI-roU+b3Q5M=F_@t1IMXVD{MYmbd>>GWCw#XZhTE zY#eQn7dCPRjuYaJ{Ct(TD3@K6$PX!PZX!A!V(_u2E%vaXBK3IQ88A{l>Q{#Eb;#Jh gGXLDa!5;z)0PUeK?fF5U?f?J)07*qoM6N<$f-}!jj{pDw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/decalEditor/decalEditor.cs b/Templates/BaseGame/game/tools/decalEditor/decalEditor.cs new file mode 100644 index 000000000..b861d8694 --- /dev/null +++ b/Templates/BaseGame/game/tools/decalEditor/decalEditor.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/* +function doUtil() +{ + for ( %i = 0; %i < DecalDataSet.getCount(); %i++ ) + { + %obj = DecalDataSet.getObject(%i); + %obj.internalName = %obj.getName(); + DecalPMan.setDirty( %obj ); + } +} +*/ \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/decalEditor/decalEditorActions.cs b/Templates/BaseGame/game/tools/decalEditor/decalEditorActions.cs new file mode 100644 index 000000000..b65fb3000 --- /dev/null +++ b/Templates/BaseGame/game/tools/decalEditor/decalEditorActions.cs @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function DecalEditorGui::createAction(%this, %class, %desc) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = %class; + superClass = BaseDecalEdAction; + actionName = %desc; + tree = DecalEditorTreeView; + }; + popInstantGroup(); + return %action; +} + +function DecalEditorGui::doAction(%this, %action) +{ + if (%action.doit()) + %action.addToManager(Editor.getUndoManager()); +} + +function BaseDecalEdAction::redo(%this) +{ + // Default redo action is the same as the doit action + %this.doit(); +} + +function BaseDecalEdAction::undo(%this) +{ +} + +//------------------------------------------------------------------------------ +// Edit node +function DecalEditorGui::doEditNodeDetails(%this, %instanceId, %transformData, %gizmo) +{ + %action = %this.createAction(ActionEditNodeDetails, "Edit Decal Transform"); + %action.instanceId = %instanceId; + %action.newTransformData = %transformData; + + if( %gizmo ) + %action.oldTransformData = %this.gizmoDetails; + else + %action.oldTransformData = %this.getDecalTransform(%instanceId); + + %this.doAction(%action); +} + +function ActionEditNodeDetails::doit(%this) +{ + %count = getWordCount(%this.newTransformData); + if(%this.instanceId !$= "" && %count == 7) + { + DecalEditorGui.editDecalDetails( %this.instanceId, %this.newTransformData ); + DecalEditorGui.syncNodeDetails(); + DecalEditorGui.selectDecal( %this.instanceId ); + return true; + } + return false; +} + +function ActionEditNodeDetails::undo(%this) +{ + %count = getWordCount(%this.oldTransformData); + if(%this.instanceId !$= "" && %count == 7) + { + DecalEditorGui.editDecalDetails( %this.instanceId, %this.oldTransformData ); + DecalEditorGui.syncNodeDetails(); + DecalEditorGui.selectDecal( %this.instanceId ); + } +} + +//------------------------------------------------------------------------------ +// Delete Decal Datablocks + +// This functionality solely depends on the undo/redo datablock callbacks in +// source. + +function DecalEditorGui::redoDeleteDecalDatablock( %this, %datablock ) +{ + // Remove the object from file and place a filter + if( %datablock.getFilename() !$= "" ) + { + DecalPMan.removeDirty( %datablock ); + DecalPMan.removeObjectFromFile( %datablock ); + } + + DecalDataList.addFilteredItem( %datablock ); +} + +function DecalEditorGui::undoDeleteDecalDatablock( %this, %datablock ) +{ + // Replace the object in file and remove the filter + %filename = %datablock.getFilename(); + if( %datablock.getFilename() !$= "" ) + { + DecalPMan.setDirty( %datablock, %filename ); + DecalPMan.saveDirty(); + } + + DecalDataList.removeFilteredItem( %datablock ); +} diff --git a/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.cs b/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.cs new file mode 100644 index 000000000..3636b29a1 --- /dev/null +++ b/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.cs @@ -0,0 +1,343 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function DecalEditorGui::onWake( %this ) +{ +} + +function DecalEditorGui::onSelectInstance( %this, %decalId, %lookupName ) +{ + if( DecalEditorGui.selDecalInstanceId == %decalId ) + return; + // Lets remember the new Id + DecalEditorGui.selDecalInstanceId = %decalId; + DecalEditorTreeView.clearSelection(); + + %name = %decalId SPC %lookupName; + %item = DecalEditorTreeView.findItemByName( %name ); + DecalEditorTreeView.selectItem( %item ); + DecalEditorGui.syncNodeDetails(); +} + +function DecalEditorGui::onCreateInstance( %this, %decalId, %lookupName ) +{ + // Lets remember the new Id + DecalEditorGui.selDecalInstanceId = %decalId; + + // Add the new instance to the node tree + DecalEditorTreeView.addNodeTree( %decalId, %lookupName ); + DecalEditorTreeView.clearSelection(); + + %name = %decalId SPC %lookupName; + %item = DecalEditorTreeView.findItemByName( %name ); + DecalEditorTreeView.selectItem( %item ); + DecalEditorGui.syncNodeDetails(); +} + +function DecalEditorGui::onDeleteInstance( %this, %decalId, %lookupName ) +{ + if( %decalId == DecalEditorGui.selDecalInstanceId ) + DecalEditorGui.selDecalInstanceId = -1; + + %id = DecalEditorTreeView.findItemByName( %decalId SPC %lookupName ); + DecalEditorTreeView.removeItem(%id); +} + +function DecalEditorGui::editNodeDetails( %this ) +{ + %decalId = DecalEditorGui.selDecalInstanceId; + if( %decalId == -1 ) + return; + + %nodeDetails = DecalEditorDetailContainer-->nodePosition.getText(); + %nodeDetails = %nodeDetails @ " " @ DecalEditorDetailContainer-->nodeTangent.getText(); + %nodeDetails = %nodeDetails @ " " @ DecalEditorDetailContainer-->nodeSize.getText(); + + if( getWordCount(%nodeDetails) == 7 ) + DecalEditorGui.doEditNodeDetails( %decalId, %nodeDetails, false ); + +} + +// Stores the information when the gizmo is first used +function DecalEditorGui::prepGizmoTransform( %this, %decalId, %nodeDetails ) +{ + DecalEditorGui.gizmoDetails = %nodeDetails; +} + +// Activated in onMouseUp while gizmo is dirty +function DecalEditorGui::completeGizmoTransform( %this, %decalId, %nodeDetails ) +{ + DecalEditorGui.doEditNodeDetails( %decalId, %nodeDetails, true ); +} + +function DecalEditorGui::onSleep( %this ) +{ +} + +function DecalEditorGui::syncNodeDetails( %this ) +{ + %decalId = DecalEditorGui.selDecalInstanceId; + if( %decalId == -1 ) + return; + + %lookupName = DecalEditorGui.getDecalLookupName( %decalId ); + DecalEditorGui.updateInstancePreview( %lookupName.material ); + + DecalEditorDetailContainer-->instanceId.setText(%decalId @ " " @ %lookupName); + %transformData = DecalEditorGui.getDecalTransform(%decalId); + DecalEditorDetailContainer-->nodePosition.setText(getWords(%transformData, 0, 2)); + DecalEditorDetailContainer-->nodeTangent.setText(getWords(%transformData, 3, 5)); + DecalEditorDetailContainer-->nodeSize.setText(getWord(%transformData, 6)); +} + +function DecalEditorGui::paletteSync( %this, %mode ) +{ + %evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);"; + eval(%evalShortcut); +} + +function DecalDataList::onSelect( %this, %id, %text ) +{ + %obj = %this.getItemObject( %id ); + DecalEditorGui.currentDecalData = %obj; + + %itemNum = DecalDataList.getSelectedItem(); + if ( %itemNum == -1 ) + return; + + %data = DecalDataList.getItemObject( %itemNum ); + + // Update the materialEditorList + $Tools::materialEditorList = %data.getId(); + + //Canvas.pushDialog( DecalEditDlg ); + DecalInspector.inspect( %data ); + DecalEditorGui.updateDecalPreview( %data.material ); +} + +function RetargetDecalButton::onClick( %this ) +{ + %id = DecalDataList.getSelectedItem(); + %datablock = DecalDataList.getItemText(%id ); + + if( !isObject(%datablock) ) + { + MessageBoxOK("Error", "A valid Decal Template must be selected."); + return; + } + + // This is the first place IODropdown is used. The # in the function passed replaced with the output + // of the preset menu. + + IODropdown("Retarget Decal Instances", + "Retarget DecalInstances from " @ %datablock.getName() @ " over to....", + "decalDataSet", + "DecalEditorGui.retargetDecalDatablock(" @ %datablock.getName() @ ", #);", + ""); + DecalEditorGui.rebuildInstanceTree(); +} + +function NewDecalButton::onClick( %this ) +{ + %name = getUniqueName( "NewDecalData" ); + + %str = "datablock DecalData( " @ %name @ " ) { Material = \"WarningMaterial\"; };"; + eval( %str ); + + DecalPMan.setDirty( %name, $decalDataFile ); + + if ( strchr(LibraryTabControl.text, "*") $= "" ) + LibraryTabControl.text = LibraryTabControl.text @ "*"; + + DecalDataList.doMirror(); + %id = DecalDataList.findItemText( %name ); + DecalDataList.setSelected( %id, true ); + + Canvas.pushDialog( DecalEditDlg ); + DecalInspector.inspect( %name ); +} + +function DeleteDecalButton::onClick( %this ) +{ + + if( DecalEditorTabBook.getSelectedPage() == 0 ) // library + { + %id = DecalDataList.getSelectedItem(); + %datablock = DecalDataList.getItemText(%id ); + + MessageBoxYesNoCancel("Delete Decal Datablock?", + "Are you sure you want to delete

" @ %datablock @ "

Datablock deletion won't take affect until the engine is quit.", + "DecalEditorGui.deleteSelectedDecalDatablock();", + "", + "" ); + } + else // instances + { + DecalEditorGui.deleteSelectedDecal(); + } +} + +// Intended for gui use. The undo/redo functionality for deletion of datablocks +// will enable itself automatically after using this function. +function DecalEditorGui::deleteSelectedDecalDatablock() +{ + %id = DecalDataList.getSelectedItem(); + %datablock = DecalDataList.getItemText(%id ); + + DecalEditorGui.deleteDecalDatablock( %datablock ); + + if( %datablock.getFilename() !$= "" ) + { + DecalPMan.removeDirty( %datablock ); + DecalPMan.removeObjectFromFile( %datablock ); + } + + DecalDataList.addFilteredItem( %datablock ); +} + +function DecalEditorTabBook::onTabSelected( %this, %text, %idx ) +{ + if( %idx == 0) + { + DecalPreviewWindow.text = "Template Properties"; + DecalEditorLibraryProperties.setVisible(true); + DecalEditorTemplateProperties.setVisible(false); + RetargetDecalButton.setVisible( true ); + SaveDecalsButton.setVisible( true ); + NewDecalButton.setVisible( true ); + DeleteDecalButton.tabSelected = %idx; + } + else + { + DecalPreviewWindow.text = "Instance Properties"; + RetargetDecalButton.setVisible( false ); + NewDecalButton.setVisible( false ); + SaveDecalsButton.setVisible( false ); + DeleteDecalButton.tabSelected = %idx; + DecalEditorLibraryProperties.setVisible(false); + DecalEditorTemplateProperties.setVisible(true); + } +} + +function DecalEditorTreeView::onDefineIcons() +{ + %icons = "tools/gui/images/treeview/default:" @ + "tools/classIcons/decal:" @ + "tools/classIcons/decalNode:"; + + DecalEditorTreeView.buildIconTable( %icons ); +} + +function DecalEditorTreeView::onSelect(%this, %id) +{ + %instanceTag = getWord( DecalEditorTreeView.getItemText(%id), 1 ); + if( !isObject( %instanceTag ) ) + return; + + if( %instanceTag.getClassName() !$= "DecalData" ) + return; + + // Grab the id from the tree view + %decalId = getWord( DecalEditorTreeView.getItemText(%id), 0 ); + + if( DecalEditorGui.selDecalInstanceId == %decalId ) + return; + + // Set the curent decalinstances id + DecalEditorGui.selDecalInstanceId = %decalId; + + DecalEditorGui.selectDecal(%decalId); + DecalEditorGui.syncNodeDetails(%id); +} + +// Creating per node in the instance tree +function DecalEditorTreeView::addNodeTree(%this, %nodeName, %parentName) +{ + // If my template isnt there...put it there + if ( %this.findItemByName(%parentName) == 0 ) + { + %rootId = %this.findItemByName(""); + %this.insertItem( %rootId, %parentName, 0, "", 1, 1); + } + + %nodeName = %nodeName SPC %parentName; + %parentId = %this.findItemByName(%parentName); + %id = %this.insertItem(%parentId, %nodeName, 0, "", 2); +} + +function DecalInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + if( %fieldName $= "Material" ) + DecalEditorGui.updateDecalPreview( %newValue ); + + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); + + if (%oldValue != %newValue || %oldValue !$= %newValue) + %this.setDirty(%object); +} + +function DecalInspector::setDirty( %this, %object ) +{ + DecalPMan.setDirty( %object ); + + if ( strchr(LibraryTabControl.text, "*") $= "" ) + LibraryTabControl.text = LibraryTabControl.text @ "*"; +} + +function DecalInspector::removeDirty() +{ + if ( strchr(LibraryTabControl.text, "*") !$= "" ) + LibraryTabControl.text = stripChars(LibraryTabControl.text, "*"); +} + +function DecalEditorGui::updateDecalPreview( %this, %material ) +{ + if( isObject( %material ) ) + DecalPreviewWindow-->decalPreview.setBitmap( MaterialEditorGui.searchForTexture( %material.getId(), %material.diffuseMap[0]) ); + else + DecalPreviewWindow-->decalPreview.setBitmap("tools/materialEditor/gui/unknownImage"); +} + +function DecalEditorGui::updateInstancePreview( %this, %material ) +{ + if( isObject( %material ) ) + DecalPreviewWindow-->instancePreview.setBitmap( MaterialEditorGui.searchForTexture( %material.getId(), %material.diffuseMap[0]) ); + else + DecalPreviewWindow-->instancePreview.setBitmap("tools/materialEditor/gui/unknownImage"); +} + +function DecalEditorGui::rebuildInstanceTree( %this ) +{ + // Initialize the instance tree when the tab is selected + DecalEditorTreeView.removeItem(0); + %rootId = DecalEditorTreeView.insertItem(0, "", 0, ""); + %count = DecalEditorGui.getDecalCount(); + for (%i = 0; %i < %count; %i++) + { + %name = DecalEditorGui.getDecalLookupName(%i); + if( %name $= "invalid" ) + continue; + + DecalEditorTreeView.addNodeTree(%i, %name); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.gui b/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.gui new file mode 100644 index 000000000..919291d2c --- /dev/null +++ b/Templates/BaseGame/game/tools/decalEditor/decalEditorGui.gui @@ -0,0 +1,831 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiDecalEditorCtrl(DecalEditorGui) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "WorldEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "255 0 0 120"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + currentDecalID = "175"; + Docking = "None"; + + new GuiWindowCollapseCtrl(DecalEditorWindow) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 SPC getWord(EditorGuiToolbar.extent, 1) -1; + Extent = "210 600"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "2 2 2 2"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EditorGui.setEditor( WorldEditorInspectorPlugin );"; + EdgeSnap = "1"; + text = "Decal Editor"; + + new GuiTabBookCtrl(DecalEditorTabBook) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 502"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "3 1 3 3"; + Docking = "client"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "0"; + MinTabWidth = "64"; + + new GuiTabPageCtrl(LibraryTabControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 483"; + Docking = "client"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Library"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + Margin = "0 0 0 0"; + Docking = "client"; + MinExtent = "0 8"; + Profile = "GuiInspectorProfile"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + MinExtent = "0 -500"; + Profile = "ToolsGuiTabBorderProfile"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiDefaultProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiListBoxCtrl(DecalDataList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiListBoxProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "474 48"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + AllowMultipleSelections = "0"; + fitParentWidth = "0"; + mirrorSet = "DecalDataSet"; + }; + }; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + internalName = "instanceTab"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 483"; + Docking = "client"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Instances"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + Margin = "0 0 0 0"; + Docking = "client"; + MinExtent = "0 8"; + Profile = "GuiInspectorProfile"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + MinExtent = "0 -500"; + Profile = "ToolsGuiTabBorderProfile"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 483"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTreeViewCtrl(DecalEditorTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "200 100"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + }; + }; + // Save Button + new GuiBitmapButtonCtrl(SaveDecalsButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "137 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "DecalPMan.saveDirty(); DecalInspector::removeDirty();"; + hovertime = "1000"; + groupNum = "-1"; + text =""; + tooltip = "Save All"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/save-icon"; + }; + + new GuiBitmapButtonCtrl(RetargetDecalButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "157 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Retarget missing decals to an existing decal datablock"; + bitmap = "tools/gui/images/retarget-btn"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(NewDecalButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "177 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Create New Decal Template"; + bitmap = "tools/gui/images/new"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(DeleteDecalButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "190 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = ""; + tooltip = "Delete Selected Decal Template"; + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + tabSelected = "0"; + }; + }; + + + new GuiWindowCollapseCtrl(DecalPreviewWindow) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 SPC getWord(EditorGuiToolbar.extent, 1) + getWord(DecalEditorWindow.extent, 1) - 2; + Extent = "210 335"; + MinExtent = "210 335"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "152 235"; + closeCommand = "EPainter.parentGroup.setVisible(false);"; + EdgeSnap = "1"; + text = "Decal Properties"; + + new GuiScrollCtrl(DecalEditorTemplateProperties){ + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + VertSizing = "bottom"; + HorizSizing = "width"; + Position = "4 24"; + Extent = "202 259"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + Docking = "client"; + Margin = "3 1 3 3"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "189 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 0"; + Caption = "Decal Instance Preview"; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "-1 0"; + Extent = "202 187"; + Docking = "none"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "instancePreview"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "center"; + VertSizing = "height"; + Position = "0 0"; + Extent = "188 186"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + wrap = "0"; + bitmap= "tools/materialeditor/gui/unknownImage"; + }; + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "center"; + VertSizing = "height"; + Position = "0 0"; + Extent = "188 186"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/terrainpainter/terrain-painter-border-large"; + wrap = "0"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 0"; + Caption = "Decal Instance Properties"; + Margin = "0 0 0 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(DecalEditorDetailContainer){ + Position = "0 202"; + Extent = "202 79"; + HorizSizing = "width"; + VertSizing = "bottom"; + isContainer = "1"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "3 2"; + Extent = "47 16"; + text = "Instance"; + }; + new GuiTextCtrl(){ // instance Name + Profile = "ToolsGuiTextProfile"; + internalName = "instanceId"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "54 2"; + Extent = "128 18"; + text = ""; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "3 21"; + Extent = "47 16"; + text = "Translate"; + }; + new GuiTextEditCtrl(){ // instance translate + Profile = "ToolsGuiTextEditProfile"; + internalName = "nodePosition"; + HorizSizing = "width"; + VertSizing = "bottom"; + AltCommand = "DecalEditorGui.editNodeDetails();"; + Position = "54 20"; + Extent = "128 18"; + text = ""; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "3 41"; + Extent = "47 16"; + text = "Tangent"; + }; + new GuiTextEditCtrl(){ // instance rotation + Profile = "ToolsGuiTextEditProfile"; + internalName = "nodeTangent"; + HorizSizing = "width"; + VertSizing = "bottom"; + AltCommand = "DecalEditorGui.editNodeDetails();"; + Position = "54 40"; + Extent = "128 18"; + text = ""; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "3 61"; + Extent = "47 16"; + text = "Size"; + }; + new GuiTextEditCtrl(){ // instance scale + Profile = "ToolsGuiTextEditProfile"; + internalName = "nodeSize"; + HorizSizing = "width"; + VertSizing = "bottom"; + AltCommand = "DecalEditorGui.editNodeDetails();"; + Position = "54 60"; + Extent = "128 18"; + text = ""; + }; + }; + }; + }; + }; + }; + + new GuiScrollCtrl(DecalEditorLibraryProperties) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + VertSizing = "bottom"; + HorizSizing = "width"; + Position = "4 24"; + Extent = "202 259"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + Docking = "client"; + Margin = "3 1 3 3"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "187 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 0"; + Caption = "Decal Template Preview"; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "-1 0"; + Extent = "202 187"; + Docking = "none"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "decalPreview"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "center"; + VertSizing = "height"; + Position = "0 0"; + Extent = "188 186"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + wrap = "0"; + bitmap= "tools/materialeditor/gui/unknownImage"; + }; + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "center"; + VertSizing = "height"; + Position = "0 0"; + Extent = "188 186"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/terrainpainter/terrain-painter-border-large"; + wrap = "0"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 0"; + Caption = "Decal Template Properties"; + Margin = "0 0 0 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiInspector(DecalInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "200 257"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + groupFilters = "+General,+SimBase,+Decal,+Rendering,+Texturing"; + + }; + }; + }; + }; + //---------------------------------- + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/decalEditor/main.cs b/Templates/BaseGame/game/tools/decalEditor/main.cs new file mode 100644 index 000000000..06dd96f81 --- /dev/null +++ b/Templates/BaseGame/game/tools/decalEditor/main.cs @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeDecalEditor() +{ + echo(" % - Initializing Decal Editor"); + + $decalDataFile = "art/decals/managedDecalData.cs"; + + exec( "./decalEditor.cs" ); + exec( "./decalEditorGui.gui" ); + exec( "./decalEditorGui.cs" ); + exec( "./decalEditorActions.cs" ); + + // Add ourselves to EditorGui, where all the other tools reside + DecalEditorGui.setVisible( false ); + DecalPreviewWindow.setVisible( false ); + DecalEditorWindow.setVisible( false ); + EditorGui.add( DecalEditorGui ); + EditorGui.add( DecalEditorWindow ); + EditorGui.add( DecalPreviewWindow ); + DecalEditorTabBook.selectPage( 0 ); + + new ScriptObject( DecalEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = DecalEditorGui; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "5", "EDecalEditorAddDecalBtn.performClick();", "" ); + %map.bindCmd( keyboard, "1", "EDecalEditorSelectDecalBtn.performClick();", "" ); + %map.bindCmd( keyboard, "2", "EDecalEditorMoveDecalBtn.performClick();", "" ); + %map.bindCmd( keyboard, "3", "EDecalEditorRotateDecalBtn.performClick();", "" ); + %map.bindCmd( keyboard, "4", "EDecalEditorScaleDecalBtn.performClick();", "" ); + + DecalEditorPlugin.map = %map; + + new PersistenceManager( DecalPMan ); + +} + +function destroyDecalEditor() +{ +} + +// JCF: helper for during development +function reinitDecalEditor() +{ + exec( "./main.cs" ); + exec( "./decalEditor.cs" ); + exec( "./decalEditorGui.cs" ); +} + +function DecalEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Decal Editor", "", DecalEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Decal Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "DecalEditorPlugin", "DecalEditorPalette", expandFilename("tools/decalEditor/decal-editor"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( DecalPreviewWindow, DecalEditorWindow ); + + //set initial palette setting + %this.paletteSelection = "AddDecalMode"; +} + +function DecalEditorPlugin::onActivated( %this ) +{ + EditorGui.bringToFront( DecalEditorGui ); + DecalEditorGui.setVisible( true ); + DecalEditorGui.makeFirstResponder( true ); + DecalPreviewWindow.setVisible( true ); + DecalEditorWindow.setVisible( true ); + + %this.map.push(); + + //WORKAROUND: due to the gizmo mode being stored on its profile (which may be shared), + // we may end up with a mismatch between the editor mode and gizmo mode here. + // Reset mode explicitly here to work around this. + DecalEditorGui.setMode( DecalEditorGui.getMode() ); + + // Set the current palette selection + DecalEditorGui.paletteSync( %this.paletteSelection ); + + // Store this on a dynamic field + // in order to restore whatever setting + // the user had before. + %this.prevGizmoAlignment = GlobalGizmoProfile.alignment; + + // The DecalEditor always uses Object alignment. + GlobalGizmoProfile.alignment = "Object"; + + DecalEditorGui.rebuildInstanceTree(); + + // These could perhaps be the node details like the shape editor + //ShapeEdPropWindow.syncNodeDetails(-1); + + Parent::onActivated(%this); +} + +function DecalEditorPlugin::onDeactivated( %this ) +{ + DecalEditorGui.setVisible(false); + DecalPreviewWindow.setVisible( false ); + DecalEditorWindow.setVisible( false ); + + %this.map.pop(); + + // Remember last palette selection + %this.paletteSelection = DecalEditorGui.getMode(); + + // Restore the previous Gizmo + // alignment settings. + GlobalGizmoProfile.alignment = %this.prevGizmoAlignment; + + Parent::onDeactivated(%this); +} + +function DecalEditorPlugin::isDirty( %this ) +{ + %dirty = DecalPMan.hasDirty(); + + %dirty |= decalManagerDirty(); + + return %dirty; +} + +function DecalEditorPlugin::onSaveMission( %this, %file ) +{ + DecalPMan.saveDirty(); + decalManagerSave( %file @ ".decals" ); +} + +function DecalEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %hasSelection = false; + + if ( DecalEditorGui.getSelectionCount() > 0 ) + %hasSelection = true; + + %editMenu.enableItem( 3, false ); // Cut + %editMenu.enableItem( 4, false ); // Copy + %editMenu.enableItem( 5, false ); // Paste + %editMenu.enableItem( 6, %hasSelection ); // Delete + %editMenu.enableItem( 8, false ); // Deselect + + // NOTE: If you want to implement Cut, Copy, Paste, or Deselect + // for this editor simply enable the menu items when it is appropriate + // and fill in the method stubs below. +} + +function DecalEditorPlugin::handleDelete( %this ) +{ + DecalEditorGui.deleteSelectedDecal(); +} + +function DecalEditorPlugin::handleDeselect( %this ) +{ +} + +function DecalEditorPlugin::handleCut( %this ) +{ +} + +function DecalEditorPlugin::handleCopy( %this ) +{ +} + +function DecalEditorPlugin::handlePaste( %this ) +{ +} + +function DecalEditorPlugin::handleEscape( %this ) +{ +} + diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/button.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/button.png new file mode 100644 index 0000000000000000000000000000000000000000..e255cb91198008ec940b5732f032dcaf87bcf429 GIT binary patch literal 1545 zcmV+k2KM=hP)qZnw}93erXiNgD-XG!bIrTOp`~qm ze01T$1-o1>+m>b7h)8r?y|`c(FJ81U517s#Pmhm}Q!15G1Hu?(G8r{QhosYKN+y#8 z`&cY?MlFHE>5IqXR4f+B=kw8a2G`fum5$IWl?v&)PGQDjXPPDz3I*D=Yu64q(Au$y zG&VMt3I>DZ_xtIYy?euJtE(w7KopSOd-jC?T3Jc)9;ju_&(DW>6Uk6s3?q!m9UUFY znDwv<&yxwku^hIqMSy<-;_5hbCrJcV+^>F%o$CM_h9Nhgdqtc8w=BKxz9FGv$PKT7 zETbBMkIH4;jjk9w^riw}r8jS~#a&CNBf zRdBYA4Gaui=AW}X-fv4OZ{w@m4^W=6L3I&0xby==fPR1_%pjEG@1CO`R5BYLe|v&F z#a0!@kDZ#Jbb)r?`Rc>7yqP}9!s(F{6KZmAXVrE`u&M)-0TFEY&9n9WpuZ!oQMj?y=yL6rrZ zYokPcxl(OHp+vz}PczFD#1b%^e$c1-LD#)GOrk>BO&7tTq!zkLZ%H|!!mVh1Kj3$w zwern->NEm;bS={Kc7{B@9>E!iaT#C6+qr%H;D7Uj56;cmiL7NOa~0KtMaR{P3l=^9 zaXmj6J^2=`r;7A?PqW) zmL|p||1(0e(AIDwb>K zI>3e*9tVcc+tWR-cONK#rmhYApn8yGqSwG%%6@)`$$Ox;oge&oKS3*-CS~%~UbR)Q zn}*=436p+#m}hgonFP(464}iCyAI#3o^bQUV-s~}?;q~%?NxMN%C}T1 zxx4Nqm7w~-V9%6)SUF&Vt#Me2x(U_IF&Ffn1<~=H0JaX>P$;CeryqRJ95E0GD25J> zeE7%_`svP{Cc{?_A67V?jwNC_qeqX{$Tq?klC3u}teU_E%V=Wjm?UfcHwRU^Z`EzA z?xp+Q8?B_3o#DOddi6x$?FYbxkhhN?APZexU20&B2M-;hyNioWh6fHFR5&&e^#CT1 zEa?2hmTv@aD{ma$28*lAGd4Ch9`HIx_&m4NLV|b91w$1K|AeQDHuzP9sCfe*_o+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/button_left.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/button_left.png new file mode 100644 index 0000000000000000000000000000000000000000..3f7238fbf2e13622f07819dd6829e472f8ab2ea9 GIT binary patch literal 1450 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 R(E$Je002ovPDHLkV1h+~zgPeO literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/button_toolbar.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/button_toolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..3de3b1519435e5edfa0206cead8812bbf2b5066e GIT binary patch literal 1219 zcmeAS@N?(olHy`uVBq!ia0vp^B0%iT!3HD?0+;IpDVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5HLOop^Ln>}1rTpM;31MMiJo4ke z{gFLCKR>_Cyi!Two41!w(hT!@xwa&gkG>9frq7sBA&|qttb8Ysfsw^bAS0r6#fpsG zU0qzv6Ve0(#S(w~daK>mcqir#PyW4qi4}i;nI4{NUG7k#df@_J3Ns^9N8-IbmERox zJpcUs{QpD-5uO_in=CwRJ>)&?Ep99jP-vLZ(6-IO!ouM5hYu4(!W6zL)GGWv@d#*{ zQ9yW9)T}ie4X-Y{iZOF2xMdwTa$qoQOnPfC#nSjcLcy%?%M}Iz2OpjgHU>tQQjle@ z*qB%ZuJW>QD69f0ZcP69eEs~CqedR`RXWTp0w+$p&$FxbnwGubSD!*RBT(SLEM|7T zKYu?x<@RiAywd!o@r!adP~o{tY|$5(yhWRq@;WdC?zMp#7|hK7a( zmgzYuX9|9vO66fLEiI8eH_w{8Z$kL`I70^R$Q}crF0mD-qfAUqC4oMCFw4@?a${BJ z{Q2@dt*xz+esis^GF_>kqbR7z5A;RA;TaQomDw7Aj#xPT2GAD0jjXJ!C%YI{*i8|V zR%loucN!R3OkJkd*4849qN$>7*5&ULG>)m&y||!Q@a8ZB$e}-&fIgSz5O4@F2L|nm zJO!Yj?KDOv4w=(oVPQNvIyx%HKh3wVm*|tVZW96;2(;z?{(65d)uM5iC*B!|g@Q_==w?u9Ue^|>wpskI}d?752 zUc3o~EPDKsJqnf#%o@f9F?%Wm7u&diV&-cE8;3$gXK$Ef8VCrDan!&gs$9VG8sjX8BdEUKy7qK@bKcC-A z?^D0L{WSJh)~qI9m^RrsFl=;)P2*zWiHV6}Sw3Wp{!Kp-3Ur^L zBX6>cS&sxm5dS6iOZ_^00uBnytqxmdEg4q%b42n0!)aa)&_=-aOvEZWrw^Id@G1{QCf;|$5goYNwX3A7(8 z;^_fK>ViEr5+MnTzIr%v@g)~BbXf!?%Y)*i@le6%mzS6SumAn6S7kj?x)a{Q!E|bF z+K%$~a?A_4xK;Rr*nzHYk81&@)iqo@7_>|qx)h%-6jI6AFtweXzsb9H&QnEL9Phly zr@zs$Qdr{m%Lu8r28ANxO5uzk7lM;dToN!{U-bsY@uI3uUSJC8PlYq}_W}Knly|@> z2w5JK241O}fs)t}U}UZWB~VDbu3GNgz`)EFVdQ&MBb@0N$Cn A>;M1& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/dropDown.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/dropDown.png new file mode 100644 index 0000000000000000000000000000000000000000..795b0e575c9023ec75fee87f42a6605519aec580 GIT binary patch literal 6848 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/form.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/form.png new file mode 100644 index 0000000000000000000000000000000000000000..cc0d39841ff4d0af9235e6119c4ee0e73efb8fcd GIT binary patch literal 3183 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/formMenu.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/formMenu.png new file mode 100644 index 0000000000000000000000000000000000000000..110adef3e214aec63cc72d67daf91c8746ca4f15 GIT binary patch literal 1396 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/iconCancel.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/iconCancel.png new file mode 100644 index 0000000000000000000000000000000000000000..744df795af9996f6c1007a592dad168a5e5c5a2d GIT binary patch literal 853 zcmV-b1FHOqP)Ssu>GBh%Ekafg z5I~H`E&ytdXXWEsAYi1)z{=0V@c;K828O@?7#M+azuvxQczfgw!_QZ5k`!kym=Dtf z5I}GPp0?B{vhZ@v6ExOhVEguzf#KC728MtC7#Ki?{QSwlz%R-0lUIh}^}!PiKVQF1 zR$jDn9!MWR0D%p7+FX~&!UxoB1l0T$sQKAF2B33+*8T<>@Ec;tuU`xd;tC8u1>_iB z9yktiNuuiNO$z`5hzVp0(4U!thMEj)A0IO?Ji5)mASlDYpyLSC_?LmS0+d=Kmb7k@y}m9R&H(vhHF5Ze*$gd;Add4@?&7&5@cZb z_8G|bVqjP`1t@=sf#LR526kmVpaH-2L0SO<2yDRrKfk~I|M`=h5oF^JppEAaGBB`k zGBCJ?Lqh7{Mh1q%J0Nn5Obq{i`~n&94I~8+Kwty@f&2?|?)P61O|0yQxMBqw_U9km z0HBLOmV%`K0tn)Q-@myT8Gy0z?Hj}pV|xY$hX4kKU27N^K7U|f2u=Y8EHGA9%wzy5 zV`2p9`NIv;3lKn%F!=rZ^6$6rjoE~O0d?mZ1H-)=3=E4VFfi=f1hfrk)2rveczz6t zP9-gdAFtkkTyPnr6(E41Uidxz)sfS4L{0S>*uQ;eV7LjCyLJ&0jUeysSPwSnkFplS zPbD3OXS??S4fs19q!%E7;0ft&OlS%#FVAc-Gb09$`&WQ20lMP1o#>L0u#@_e}BON_x;UV zhBx~UGW>l0GD+v`Rd`Yc2p~rE+<4Q=DenKDKfuiNS06}!XFz18OF%qb_w2QW$m#(C fh>_Cl3=m)dj8|3`UT=(b00000NkvXXu0mjfz>(}qeiv0g2&Ey3b_}G}i;vc{NVK{vICBx-sU!OBD{?9$t=)4xD z2Oxmp2KY_C8vN_~?}e_aT--X+ybMp@{$hCX@jJucfBzZyxtJNG_&FGEzxm9te(&9{ z3@i-srzZG;4FL!sumQePE-U^1{pYZ&3b&NH1TVv>`yUwo{Q1qGFE7Zz0F*y>|2+dE z6C;DRBoD*m_n#Ry?7IDo;r~C|Q?tVE0R#{e$P}QTV-)%ROKC{*F&w}Bj^Wq$Ukty0 z{$ubpk!0{Sm16h}H0;;UUkq2Dd|*%z;b%}&;gkCR{dWvVFF*i+4fyr+h3FvI;9 zUl@M>{tYznFVN|K7}gzs#IP2KfBpW&@axYnhA-d0Gdy_xiNVG|o`Lb#uUL>?fB*u= z0?4h zB82DR3Lqv(Wc+>l_UjJ@VQyAnEHQx%0~rE|q5nY5e;ED&z4j026@G4ZhL@i{GcYp# zdkWGC5I_(Ee*IZ|=;{*&c~Nd)dI35W=#)SU1qMwyK?W`kR)*X_eQ?rwk2ipE0oErZ#7^=%M zJoxa5;ojBT&w##I0@4Z)K=6cQn!F&xLu86bcd(Q~6&=nUom|Nlk-Lo$xx-wz2O4Gb_wU}j=i s#K`b_*~#_QD47W$fEX#w&Hw=h0H{BLdxllh{r~^~07*qoM6N<$g3ZFYTmS$7 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/iconNext.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/iconNext.png new file mode 100644 index 0000000000000000000000000000000000000000..42eb8363bd171e3e9f9b6b7f959e9f276f5bf0fa GIT binary patch literal 783 zcmV+q1MvKbP)L;O6FL;Ns$90Lg#+_>tlB=g$m}9z9}s{rdG0pjbq3aPU)* zK7arMD*)kLyLRn=`0ydfy8pPq@87@wpFe;8|H_ps7sB)b1P~L&hYuiSBBM3>O~gyRPY=s zdG`e%fG`Yz+5YF>F9uibc!t0K|1tdi_m=@E%kb>uV+LIrYlbhszB8PCbnv5I3zHB) z0HGKF!XVp0_y^F*Ak7co-Ud1uCD5ji(5S+YbCXiyCh2Qx7-@q_XL$U7jH&3sq*-%!q$@!+j}&wu{=?+Da<6Qlgy#RC-!_S{T8Giiu!5|C;()jbw z4~C`p3mG_Bxf$X$CxUHbWn~4s3}g^9Gc!EQU;rS1;9>Cl-%rNM?cNMBBAP&het|WC z3fCl_vU}tA%0H#ttW4fz!q9+qW66U%&np zm@g7w1^@&QJTCwZQD8vmb4vpC z{AU0NAgBTVA(94#3=BN75TUC}7#P%p85meNfzJKR!0_ZW1A~$i1H+e>3=BK#fG+tB zHUJ=iSiq)%G=uyK(!48!fq`9!fx*5FXdBSqYoZw#)VvuO^nl)67{I`w>CeFM<`Dw} zpETG2fB<3vn*5I`V>TC+>7Qb6j}rP;xH{{Q>W@aqo{|NaNo%*oEgz{tqJ@a6kIhA%(<^51*@ z#bDviyEDwPw($c55ED?nElAh;Q=b@s`hd3lXULm;8fX)c1JuRJ0*(1HV(KQpkg zGBbSt`G*Z4fIwd0`tuL$aIhi}U;?@KKS(ps=|K1cX!G}<|A8+24|XMpe)jeU$S_8L z0Ac|J*k7Ok5Hb9O+FqMr0M-md48Q;WXZZFL z)0!8Y2YMUmXGWOq3=AOG{Q-In8X;gHARq)ZfEla@Ab?mHz)k|0{+B^aTpAP^koW`Z zfI1my1 z24XQUd<-5c0d>W1*Z>?UVs2%0b2gx*Pnli z#>!ma3{zk|hoB3@d4VqY3%2k*KmakCW$o$*8l(nvfhEvLL6Eb+QTP8JIFOKOSj<2o i{r_hMMyAUE0R{jed~Sra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH4YhhE&{2`g8sOgUb`<28N`> zAOGhI9uW`}e8>`X$l=VtXK!y?D;+wtibvq^!A}kwZwG9=@9^i<+1cjz9Un0;9$~tw zbK}mPH<<|u0;(DsFODr(d9vWokB_(i1Hsu7Cr)r}^YZ1@`S$Yi^0m*;&yQ!jvRO9c zL^aSJ5iv2gn>w`*4m38~*w_TknKOsyk&qD2`+NIb3s+6pU=YERp>eoh-hST8*RO9g zx6Wr^5n#T+FmtlHe_rL67Z?9;&%gig|DT_q>jQSw{Qkzvs@&ap<<8FHbm4&YCJqdZ zdk(Or+$bzAHZCYAFkqPLWAI?c%$br$jvkeDc*rW?&@<&2lLN!X2B&YwdZoAT+_B@q zwqmB6_xIO22X^@SgoTBPDm2V+;89akTQsMS7ihOIb1Q>Fg9aZkh?v?Bu8rQF*ZfF+ zetlspBha$FA08fVU%TYipMQUUpH+}jXn3Ic@nADMd*erK*BTkOHc3LUw_cd z!`u6C?1w)`yR9b)39UN8)tIz>VN!y)P3WYqcg_4W0&*Vn~HBSOCMW=>Ad9o{qiK|ng;>wzZL!#foW85tS#0|NsO z-@A7&*kSK|MkbCad@bqc=2$jPpB~PYVa==&-OQkot(-hVLgQc~*F4kgYhN0_FtG^i zun`av+QjU+Ve@9;SUHBT4X1%&w8%mX=o?m%O}Dq_vp;8@@iyz#E1vCn_gsL%d*zLt%sXzM7v-MU@{uK6WptWDsyrV4lau&Mpp& z^5>EpXE|p5{qv{G;VCfq4IPb-MknlT50py)hNZgS9ER!a(9n$7We_k)8W`-4B7jCt z6F)z{Ie5a7B`SAV9faRAum;93i0tMSW#0V&XlQ~sFvPh-jI$~QWP2m!TwE4RVRx{- z-PpE=okKxv#@Q!8E+|}}PKLN1WbA}etrt%l=kB#Vys())P$Ws1`6(+f1a{Z}<+>n& z!Nb7BA;b3KDWhg0&?h3(*+DT3@|4d7AIGpthAY3B6&fTOS(sIK3Iao>52!zBXTd)k z3sy-fsYy?m5+=(-qgrjJ4a2LOd?rt9fywtkj@oh{_b(rcnIgx5Njoq`4(Oeupzwh* r(QF1g;t0!Gk(8;h(DHx;77s(OQ$}r;`k4j5QiQ?N)z4*}Q$iB}3INN1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/panel_dark.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/panel_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8ba559c7b506166e2357fdec5d2a4306b0c9d4a8 GIT binary patch literal 502 zcmV>$aav; z(kD-zJjlbtBgMhN!Jwq9%<%Nd6BPZ@GBRK>peBZ&KYu<`QBko63O@k|ASRHr_wCy^ zK|nx23TQh6FE1}x11BdZ3WkYsad9#5@$pGrxpJisq!%E7Kwe-I6&1DP=H_Mq*$#9X z!GL3CW@Zo*6LSEC7eD|pQW^^Y0R*y6<< so;{@?JwUZT00M{+l(s3!jQ{}#03JAysYeIyE&u=k07*qoM6N<$g0oG*+yDRo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/panel_light.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/panel_light.png new file mode 100644 index 0000000000000000000000000000000000000000..87372edab75fc0c89be83b346cc0dee09cec92f9 GIT binary patch literal 481 zcmV<70UrK|P)NkU)YLReP*Bi~K}Ch(!Gj04QLIu>0E>P6_;Ks0Q>T^z^=tQy zmcDuO=0P4F9w|;vP6k0CA%<^XzoO{p;^qd60W~rF`0?YJh=_kwl|G$3y`v1?LKmS4Df&q}kKx&>md9n#)F+c!;3{ZOf`0*!@=70bGVJOCC0Mw9A zAiV$q#7Jo@00a;dC}h8U{>%Wh9%KgugMmJ0v^h>3wh z01!YFhXFtUfx`eC=LGzVD+~Yv2<(LqA3kg))#kTv-);uc00G1RO^GVMfB$}pE6&ly zKx#pHfS3ayfWS!;2n087+!*!l-McNo#PkcF0U$M-Hf@Rm=>drX1P~+C0J3u};275fduum1o4{~aU_ z5I`(Y&4LprPV_W3HjWY$6m(R_=?pFV9pefsn=p!jy6 z{Cj`^0+|l7S^CnYO9y#)c%(QvIT<7*B^lnmeT(8$Az@*#7*G?#j~_pt$;imq1BIUe z1Q1A0=J@gBTOK`n^#ASKxBtI<`SKqWF5ka@N5RNqAT@XH+}Q-O7$AT^USJay6}98$ z=4Jrd4s;s9fMaH6W{{AOZ~%oDKmajP8Vdjc1hP)$_3PKSK`wyV&H;4EkMG}66k-zt zx>yw;fS5oGCMG7LoQ@3u1P~Jgg#aLcC=LUF00M^rF#2wDadF}GFRm~E2q3T*K79DF zl~kMGy?eJAL<0m6JSBeq{P`dY3yTynF8K53&of?LUT{hT2p}eCrg*z!$Bt5vOF-!# zrU{u110Xd!ckV0&=>f|B00Hv$A000{WU>HS2ShyVZp07*qoM6N<$g4K)4 A*8l(j literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout.png new file mode 100644 index 0000000000000000000000000000000000000000..082051e86df0eaf8a157e08a57b9990ce22884f3 GIT binary patch literal 625 zcmV-%0*?KOP);B>KH&_W1b;M-yF`v(&;zqs3{9A_Mx`~Gomr@>Qjz&P)?>}dAIU!(W0ij?Rp{^iP zRn@0nFMFC5M%NY`4S+$dE|1f+8z2^jHj8j^wYsQU6DJ8w9eb`7Y4WwYxc!PBhs)(* zB$LTVvm$eO7y;>#)K^8pC!>KwGs5<9rw{PK0M&og%T!w4v|7eP1P$-v5!Kxy! zIn+lZuP!g>Hu#>`T-SpR4e*_o+ohpViah&`d00000 LNkvXXu0mjf1j8W1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_dark.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..0af73e3b4f5f5da4b4be115ce416bb6092ad901f GIT binary patch literal 953 zcmeAS@N?(olHy`uVBq!ia0vp^3P9|@!3HF&`%2dVDVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*9U+n7ut+978H@CH*;nfWd{Coq?&N z_2K_|w>g`7dj5YrCCtn*gHgc2hliPmfhpsFEQ5dp^E-wH2B`ve29^sA)(i>_Y7VF>owkEN5_F;N8JYlF|;=&5Vana4ubz{|M&Ozy4mLWdd#;r z%D(9MZjjl)kmUG%=e~V)=Ire4BGS^*5p(9xpZ_R9Oh6!kb=G5D4UG(o(pOi49d_?y zmi@@A!Y<%&BGaLQNuZ#(*qhOiSzKH^V#3_HbCr5>*!1<+U)fXn`PYI#9ySKX78%wY zhwxjBwdWZ=*e8EtS;7!;V|E~JN}LJIpI=4t2WXXYwi=*w5w zolpaen-8}H6dEREI|p=oRNdWG${raJu_Pl<8W=*~BNGx9*iQJ#a!rYeLqToH0!CA@ z`}1t8w|zN1T|c_<&;1adZ}%099n+kpm)R#20YjeS$%Aas`;2V;TCHmz-F7ezDcIV@ z$i#7&F=O4rpAM?P=&mqh0ml4!R*>Trf$2j><+!2cOQt34?4T6E&xn~=TqZI%EM2BA za62f8&4HoO+i8NM%arG{FBUUSvJ23vXACKByAlI5X@kHImjiW9pA^_VnPi_ZA5mM< z0!&|qjRXE7Ojs@f~~6a8R+`pP!FMz~RU2l#&&FjKDncnhEMd35MO@ W11z7b^=$*@JqAx#KbLh*2~7YHt5Zw> literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_plusminus_header.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_plusminus_header.png new file mode 100644 index 0000000000000000000000000000000000000000..89949147338113abf7fc38bb5ab23b160613a483 GIT binary patch literal 2955 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_thin.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/rollout_thin.png new file mode 100644 index 0000000000000000000000000000000000000000..6c490366852fd7ddf095b5f91d2f6ded3fef1380 GIT binary patch literal 3133 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/scroll.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/scroll.png new file mode 100644 index 0000000000000000000000000000000000000000..252200f93b97554bde9af64bb73576844b53ab3f GIT binary patch literal 5655 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/slider.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/slider.png new file mode 100644 index 0000000000000000000000000000000000000000..d13b9372d738f12e5ddfd193afb207381796468b GIT binary patch literal 825 zcmV-91IGM`P)H;lO2R~PT~UkHG{_eVG6@Rt$t*CUo}!?qULpvjKcI_$K=e}3 zqge)mi(8v%N|6z0sF}BQ*VT2M%g)ZurE`w$?(9028~6}-U}u<_&-ZhF{PxTljw?_M z?9kvMq46yF{xUa~XQ%TPS>+t`5aTt>%?B|xIYqAA=ywJUkb+&jjp2c zWD{(KN$9dOZ?!p>NQjxX{Oym~&aldHJGvC>t{ z+vX{_L7G+U)AuYha zd>EtADmZ8b!>)b^s(?j#0lKLioyaGJNsP-AC=n{yJ<7Z7M68N1f`TroNFN#54Nsi;TCClY)bQf#; zbb;N>1w~N=&#$BX^QClv^7fJIf8_0>HyCfD+hbXz9!CS-bRY$W!(p_%YejwRG}$W@ z_Kfm_t2J;AG+Xl0z*P+K4UD(ZeYaX{K$CD0wShVokR*wL=lw0HBVxDW#e;jdS?QGj zB6;DDpMzuIY!)y0uHd6^0zZ;fj88Z7@$;9)m@5F^o)_pXe~mMJ%}iXO)}rhF9mb_T z7Ad~QIf}-DUi_?lij_C*Ogy7jXLxhpQWNw^&)}Uol_k!Vnp{^R4(_w2tf`wN6hy00000NkvXXu0mjf DZ%l== literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/background.jpg b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5fee24333186a99b0f9275479e929f26cb943413 GIT binary patch literal 101809 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�eErk^j?r(nMLTM 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
2|@uUfVV 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 R18>!hcQr#2sp{`9{tvd_rD^~G literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create.png new file mode 100644 index 0000000000000000000000000000000000000000..c3f868412002498bd40308771353b01db6819957 GIT binary patch literal 35059 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


-oz<@QUykmK+}(j#x6|g9TPFr<9+ytR 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 mpooD{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_h.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d8c0ccd5c0ee8ef4a58b720ec0b11ca2c809f5c6 GIT binary patch literal 67234 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_i.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/create_i.png new file mode 100644 index 0000000000000000000000000000000000000000..5c21094d9517091c9f8d5a1e1e4f670c321053d5 GIT binary patch literal 25395 zcmWh!cTf{w7u|%8fD}T*u!s`=C%0Isn7{{jWPcpiJXNaLaP_z}%F28Qd` zf#3pX<;$fT9*>@Q7{9jlz*@a|2^d&8xOj-`Jwie}rNp6M@0)4>fOWQ(x+>DkVms50 z_yUb8A81`^m9mqtqe6b<0NB;Fz+n1!+6F;^IpA9d&$8VoeRq`gqp^dPj+3*^MB+x{ zw_1}jna&jVD7z@=*irKW<7=-s;d4g;7e}jeKljV1EsDARwvbO+*lx56>7U=1252W$ zPCHe&8_$2=y7+DI*YMw@w$NB>v~={h;d`eNZ+-3_Qjt%`RL;hZe;qT#^gNv2FIap@ z{mnmNTba}(cGX-v{;1SuK8|eTl020=ZQ3BNh0U{8-Did*=lmv7 zH#+5te?2n$ZG!t=HlVYi+Fg<}_jTV4<0{QFkmg~cfu88uI1zu$kK27q7vsd@_IS}v zP|(E4rtX(#XCz>hYH5b=1(X-k|8r!HbbnT{iGi;p!6xVg@ z>#lvE27~N|7BN(g@OdV&+ez~a-eEd1&`xQsC{k*e{npYE*Xs%6;k`MlCDh5}De-iJ zm`nK-)m8K{`oUkqJpRCwo@i_ZJHAC1kF)ra4)W;IMNGXcJhB+|$_vw%kr?=_wy&3x z+V5Xe{S$1`5 zCJ!^=8XdcmoLO)a&ZM^;@PqT0NsFCO%q;MRqSyI-X9!KqhW=#mv`=*N`f8nz4`xGT zJ2u&+>Q`j!a~_SBiq9>er2^|2WNCUhW>bq?66XJ7tXV52ow7kI=08dn0oj{N)JZp= z&|jTzf2iG(AV&Y(^LK#Q+*!$xcJy$-`TqU6xSuyEW>hGew=fy*3wkj3;*F&SE<0>v z*DKapy)*~KvOA~pboYetFVAJl45vz}md&LcHu@zAoj=TLGxvs4YQ=0ARUG5X&owZO zjvvIMe-QD@Elz)@EQ+2y|J!lH;-}n>RQB;;{7+0t@Xf1D-4xkpTXxxgJMP)A=Ze|N zhsF9z-lG-z0sqE2B?nt(>>9F7TN2j2SNyXv{|p?A11#*=odnNI(yI6!?LPVnwAg)Y z?Cz_~+(5S4aT>9ky`v8c zj%wE!MBRKvwZ~%P@e)1pJ9fx%yggDOe1^#?EOh2UItIrtlD)CA2~>f*ih{7Ty!kVz z0~Q6(Uz5=4Zl#B-db2R4ec?f-8N8m0#?PU;Or}@V^_hxMHT$LRB|IZ{XHa&^i4X)` z%YOl1-!`J}BY1$Un{XEfe~JSf4U%{egMRd-kvto(Wpk9-1eG)qD^+J?Z-95TD zrW*B5rzbARwV%0e*H=idlM|f7zUh!=-dCp|>D6aKVbYu{Q|mLta1@;ddmFjL6(Q=( z?wL3lQDAuNIc7fUnM_7FtaVh7iVh+>ZY>Av4EQI^(wB;#Uxs%76COO;EC4A@=_}y7~dv%8ZJ`N4o9r*0MLymQF*YE^rt<^?dfg*HT`{PDSxxDr4is?Qs{ce^UH2&z`e9W=pR#iiUjENtb`HPp8KJa^ zG+Dn@HjMH~MaK%)Z;c)okbXfoV z|C{>)&Q}An+5uVxSAe;k{Pu{bfB)C%&(^b_Pk$NQ$~#uvT&9jm#z|xgQ3$eijzeWx zK^jM3ZpC4E9Q=6ps0B;?t@V7fHEW_`m5McQgb^Z(GtUp}Zhe+($gjP;*8YODY3ix;?p|P ziI?a>(K{w`1PzAKH9AOj%Pz7#zxX#fOac$HV#SeSZLyOK!u-B3sNy#0-)f?C_<0Ow zk)@{0cO?PBhn)G;D<@#v(|*&IH`KH?H*pUw|7O4&UTb}{yAtY8i_%&5rsx%;XHpFm zV^5@mLL}-bnBW~>u;&yBlp5fIcmdQ*qk1@!t|MU0s^>6uYW&Az#O#EKh%DV+I4)#1 z1|-)8!h@($LV4;Iz{4Eo2ZhndoQUx<{s-@8%UoUwFnX>f{t)AT_Br7l&b)68jgj1r zP*B;y=(yaMc;Vt^!7W?H$mA}}V)?nLMuJsXlYNT?=$Mf=k%}pDL4%obRa|i0!;!w2 zg!d(S;alf7hva5OvTu;RsF7uq4WF};Z|`umH9qj4%yD=SChXgsHc7ZUSp)+;>@>AR z#vmQQnnugFPqk+n3r3@auFL~+0yiU`wL{dftiijQ1C%;ch@-jPVL;$!)!5G}Hz$1b z`6P7`pF($v!~hfC!%AUg%f7#9)1A~bu!Wb70(b4!I&Yj9zU9h*(le~cfa&7|R68ze zbTSg^uf*~A=0!ydKk|zHaHEf^^A0qxkIlio6+LkurPi;eFn!o>cwTx{0W7`;Ht15) ziJ=c+r*+hqxA`kx#zO0v;z@3|q~gqf{!?QnE%ci+ z5v)Zp3!*A840pX%Y&JoqXjCUX<-TLm=&hP|hOZmazrEu#{q59b_6J?L)wS9G9b*Br6wu|$n&OfE zM-P!-;e&h8*wC|bB=dR;K8m9=y^Q?Z+ILYR?YYJvjKnZYDJEgwfc2phqJk-uo(VK~ zGN~2AJ~rLb>e;qv(?aT`P!mxSg#!b+6GmxBnJX)p5m&0qS46oRljX9Kz=31o%9Pl8 z(pz+H7#X*NX5a__88A5lg9CsT=NNSe0 z$}N*h05Qx-kR-lY{YiZQZ%C8|tF*^`o+w8)fR+TQ-bLhnjnNQ>XyueDJ|jEEm$`I}T7nT{BA_WhD9t!fidjX`XR$9?0UFLV)(=R3NaOO(06B zNVLqn^*gt`k#mzrb~*Aoqi4tHfGPjzDZH%fnMphOQ&;A~{o-La{3@K@=;u8Rpyx^ur+)^OdV*5Y;zrf2SmP z}~|Crsum`+hQYem!wvQHfDUnii4wBn- zqHVeKskhi-!tjVmpz(F)L5h2p7q#3>6?$9@_uou4S5FngArdWQ>-d^3CgE=8cB19~ zzXK&LL$J>>O6jTgH4bUt`O#97XbJ=LPZwm22X$=k;3ggge?dwk1IO3b-w4c;<=Z?G_!Ur1Q}x ztf;fbPqYK)p(D)WE0?>_`OrAGuSJIfx*6OhvfW_n5~PmjrZ$Tdkt+}E>cwyZY9io$ zo5FF%TV7$JloS*U<&=J=7Msw`HxmUO+}g3FACSbV(qSO)PFC+tmJ};b;FI>4pJOhz zeGFw*kgBVMKe9hZbWZ9Ol$@XoiWctTfd!^T%>2l^WeX+UjU6c^9%3*V`j$!~Dt%Yi zhwG>@iRUUP2ls0%`~2XkW8Rc>N!W~ncFV}<$OPihFB4>x`dQNd&a8iW;EVBHytEj+ zaW;40wYq!K@an&EiV>y}1!#1h6U|^+5B&=kg3G`J%!(R6R@;DCH>-+q&9M&28P`Ne3DR^y|9YE#TL(em!0MfQe-^N3-NA zQ;H}2d!oepEv-&=2zJ8}rT_K@uL=A_G1m;o!zg}X3G>D&P**M2goD8V_8JGm z+i9GoVck=Ebjn+is<&L=sm!du-*bs`|D}fX6~V~bbkA)ds;hd46`}~^@w*`)qU1$w zdC<~OKnzl^n55@G^h;eKZrn9-tF1@cHd{_BV_f2W86*eN3r(*kI%bZ#vGxd1abtcc|lgm2X z*7I@bv|*%pezndC*Wh=FN81ZKW(b6@ZilHBIMd~q0mg08mAG{+6kF*+#oRfQzpZiT zKIyXwOx4Ph@2(nrQ3(Gda6UzqdZ>OBJ4sk2UQ0kYC~>{*N{|fP8Kb=7R<){o*y}az zK2mm5% z%QL<}CH4IAN7k)dw={+yeivQP7{17UV~rM8eUs-i(T=J#zfvFl$v*)y{Tg^LuM_?0 z)6R!GoQf0ofU#vEInwop`;A%H#$vr$U@ts~-#O9ai^+=S;riPA%)adbajCV;I^V_Y za6EQ)iY3XIS0kjN1KGJJ&K^VPj`c8+9wKo~yeyC>Eb8nlku(VGnBgg8zO5p(UktHF zTzyI`Un92+Z#X7qZ41GNH2w*=K)3Nvr)59-Wxybua`J55}qQ zK#lGp5w9Lse~`LT9X==XhyGf%2Jdp6;}%*0fiZ8Fz9Q>b*RNaBm|FYDc>wPWqtGSE zfApq-N~L+OKduBbcfaF!8lVy65lK}0n;)TM)4^Xeo!Cw6Ci~P9$NOs68m=&w)FT`& zgDCN$lud=JE_7IiUth2n&DQ)S;4$l5`3)jd3~za+l}7s|H!NrgRPX{O!W-;xi_|eU zJ+x7J8CzHSs38B+r+HIn0HXKM3L$Fu`^99@fzDX{Rol0-IVzG_7>8yw*Hu^Mx+z{4 zbvNapZBp0k-xjX|E=p$K;)2CcVl%{gpC516;PAiW?1kcD0bW(J?Apt=1uk)b^R(mH*`DwD*QuH?cqEg>8ciKkkeoXoV#P%+Y8C3~ zv(RR?mL(()N;49?mKD%snI!I-x2x8Vm43p$e=adj01&_C?7pM#)Zgze6aAqZ&RZ5n z{JhMvZC#*eA=<9t;JHjtbml-32oFIKEOJADD%tFP7e3PJZ_k>wd7|KrEwu|Ztq<(z z;Z5T5aPvnm3y{mSu3FJ^^&i9ric1Wbb(DXo;z>^uiMFxJpVLz~JiUB)8xy!>k=@3o?GU zMb?kEr?$UyM#0|8+?VW6_RVg(I3ueBf3ay&RIv&B!yZ{eej1AUo=JWhHshWgx)4*e z)i+?0@&B@GI2pk?2<(lpS|{D|umDF@hf}!+tit9vdGC@#zi4unk0$-bFnxg8u0BZ0 z@)a8U%H*HT;}URdPFU!0H#XXDsW1ENN4C=Oht|Iz_H*MbG}!i!=06l2O2&gpB6W!dWQbj3L}q~3&=05d}JLN z-a1R9E#f&ig)-6+B;6+KSyExN`PlRSm$pIy{|W>CEh&+`hZ~a5N5YgnlJ2gEFkbZ! zqlUxv^J*4L6=BrTqQ>SW50s@duV&sl{AHD119PVBjRKaq^diwhszJf4I$Ke8(@h8A zMOQcego`5Bto%}Pt0j7yEZEi8jo6S)6x;`z=D1J~LZNk@X`!)|X+njE2gg-rzmuPH zZ##ZWn-`gbLwJ$3%!k2#FTYJm4f$xdPK4@S{T6$m@$VucH_9*gzCpFz?QP$=li4x0 zk6x?h1y5m^InO5Z%&zZcH#+lmu%^~cL)3v1_liQ|p-PW}g0LTBdH;0YI^S(Lx|pX? z`Frq?w95#gO^AK5aQ6Ic;(2C19RvjJdQ3g#b``sT7?cIaldg1Q=il+h4cPRNY)QQB zE@N%_tE&0Fg`Y}NiiJe_3H4DQzDo0^S$){1g`UF$_a+Gt+hdY!jJ}%z;!s}iGsS2CIGRr zP4u7y@Vs$HJaUxpu?OVTp0BU@9p2=9y3a@?L=zk~Os!u?1SdLqX5vSawVLO2+ZY)~M?k^Y2r<{+4Er{%v{97r0Y2_yG82i%p_04$x7<9zDBr3@YAD9;K;A@MIq@8= zz$jEEvhBCCBh}rv%UMnlJQp9O_l2-7&@2;~d4WDPD?o7+#(8AA3ZX=W<8BQvLh7Gc zX&{;!u0$np2jPR@D-}nS8gKbkluTLi|CTvIj`vzia!R*e{LRdgW_%;iMawNV;V4rS z?ks7&#iQUf%@Q2hvAyz%e?j3=(H)Jh(V-z3+8Mzj^PX3rCXZKO94dhlR|0}Ksc8Pt zJm{ol2(qCJ$CE*~=Uv_Ck324JI~$yN`o61Rc^gf@)UiZZBXH{ zVC_CQUc?T>Foi5AHxi`z0^qj|3|fuy7cW>uNQa6)XrU!xy{4{zFO1d=+wq|6cwRBJ zVV~jtX@1)@3`8oX3|SVD4x~=}*8Tcyv}&w95?Fz(pxZ{iD7B{m>W3Q7tW3>lDHp8} zOH|ctq4J8MU+sQbBjG;3#U^LTM z@;*`Qs;S0J^hrmq3^AUc{ch7vk z^UTO}LX<2shE{s95qUzvf)t)`&6SF|>O%b$y_kr8K4A^ZB5#ZoZW9*mxPz07e+A`> z`w;jLBuRnnnHCeNwMj=-$`|=RpO(rQ|~drT23|N;ioX5W_L> zE{)eM;dUOr?h5Kk3{P1bJ=3|(v!&7(c~m$}9P+S#7Aqk5clL2$80Yp(25lNCCtlz! zS>Ow84SU;C$%ahYKk|=YvG>+IMo|e6BI{zDD`G0u=kbK1^V95oG+3lI@-iOCrV9q=N3G|hW&m(By*LOYbtK*_=3y#`>#@kmp7|yPXTnQdO ze$s{QxNUVdG{YkqQ}7^8>f_;HJN!>?dC$7Qb+Q3tZuA#(UFgd_$PFiNzk)EkR8USb zwKLlqn<8dnDLnQTD@4qUl$ECpgCK{C6j5__)0AeRD)NU-KG_-sW=DP5`XI)5OSyli zJprdZcAe&-Bm*o&3Y||()RZgChpm9*07`~pm1&Tgz6h_LHc10x6inFZOm8fNS8Lk_PW&BY-;yR=HcBYA?^hbLz|Ix9^doGe@}z2hmj(I))o4%EojUtWoc>RO)Ym_j=*_f%Cv*L*wuV?MmE+- zD)d45X|i{wYRhwoq4(P6V2=?U*?4WzrngY1bhC-8IB1YZ1dBisOQ|kleCGvGDY~0ga<5}MpqkZDt zO`Mp4hVb9wrk;Xesc0Fm_=71|>Lx1)R|kuINP(T{TDlB@~$FoZAc- z7^bq)mOCEsTp-}<0t49AlNqUz7!9rsx`>VI<(Z>4;CCPX?)3j9{?>=CXl80V0Q@#K z$Y>B}doLYj)>$^Nd$z{J!?D2*(Jaytc&Dpt-X>vf*7lL*>S6@)_Jl~XYn15IlhU#1 zzQTQYu7%a}O3N#@D?5og0c_j|+(&iQw3vXX!lkEcO#!8VTa90GLj}tYGrUHHAZtk( z!w%WNj*FtZTqy;H26E#p*Pce}aipXj7&|xC1c|6=7ZHq#wH>5wj@s(!!gm_L zFDWxb(S(ivLV~D~O+MuxV1ssO6>7SwDww!)394!A*7?FMWem~eeR}{(D8-wanIsQ> z$1p5)Bgf$70B2kIRe5ksEPV|j20v%@w#PdGnxHDuAyw1nMaR${_>433G2GHJx~&S_ zr%CX*lz}`TZmFt`-tOE>nlN+zyYpU}M1Jpi5xA%vsQ~x;rDZB4(zsiWGHlR=`+_{3 znM`GngQ$bXbHxJ0ME^M-0tvJdCVn+*a-^rt)>~AR4d``y?@_nMD0|NmxQ5>}svO+9 zh7`9STy8&XpwQgO{``{{%b0Ae73FD(WxQa3E4((=rg}wrLrL&bU{oXHa<1%`W1Ba( zwD=!yF|I+lkNIk2?nE?i1uZG(h2@N#gqTq>8$?>ZQPeZ3FY=N)Fs=`c1rr{nsQe99k&{xvo(pj_+D&)m z^>~FXARr*k9~`w-ur);vrPDk z9aC(tlv-?zr*HJ-dl((bNg5d26w>s&&V71AF^EEjF_91|6VT#Nzd1pOT%0c4c%-dh z%5L{$r-(fwGKU@nCPS~`$9dqE+CEDPj@wIWDLypZoEkDZyUS~G-XMCQg9l1Hrz{9# z8dpU3GAYnsJGyw|TJ!7~7_Rw2ay8U@Vr(K%-C#-SXIVUCLO5Guf9U1UGn&A5!W)3LPX@Ty3kw#k(FDNL_Xrihr(>?!%PK-z5M1wUIjfGUo zS9yJ)bKrd>xclf}Jg76UuBo#Z`ULgW#L_T4UTT?ln}L zfM$#W=L11n42(kabGuBIOh4>>76ba{_gys24^Zx6Y?|Gb7)rzx%Y+L@pt^MoJq~h2 z3TiOShfvyH=O+8D!iGsU9>0~gS!j-`R1Lza;uPp)5(|J;zC1 zS+Dj|7C_t=m%=_6pchz5_f`4*+%k`*l(>JKus7W4LzlG;0+^m zT$;OeW6G_2Nyg6+EEV}Nh(PtuhLxRz|0f?x07=jbkMLQ$D=^=F9(ZgD7_#e%{1Oik zQ6lu$5pa|YacZt@=1|5#b#kP-D(d$Piz@^}8R|LMTVG*WJP()5b2d`4TR#K?OREoZ zeez=j^xlpuyMhk1%k_N{3`IIJD-+zQVzv^>MN~;Aa&gop zQC{P1Ci()gCQ zK5dfLr=usO!e^)6ia>Ln@s~``ta$)ZWR73w(#F%r>;!-{U{t}+E*0{f< zV>_U(F;wYVp=8VF_l1D@O#yap{(CuuL6lMHQ~+g!-T<_WjUkLu3+fSy39{)Lhx$`x zINo$%9flrfSUv?IV3gSE#oJ5_J4gdl&hO&hCUkM&6%Q`z%X}k<189Jawsu$>A2X?U zlI;NnqpqHE!>_Bjp}bK3BQysNo|?b}6|n5piq&MXUr9IhNiVue`sVN4lVI6jUmPBp z^|

@pX_BCc~HXhSg*$Qn&HELTUB-Qdi6B9X75JcG=n>k0a$u?`- zy-1D+Kz(u2kKmVdnMRaC=gj&&&8&f?Q`2RAIqU&9biIDqu4eWOt%0G&+%x)lAG7S* zsfi@Me?gUDj2_U>-3-Z(q)6#J4hmW=c|*K$1m${oWUbr0z(juP`B#b$u?slbRoKaP zWf7`wfC5X;xANEI6%OvcDth0--!&}YvGpm96bZa$29D&%Xl;zr15bRqV2ZzXGTunZ zhw$GaQ+c6ez+n!U45nS<@{YU-heFkGgELpGRWf z5%NLg^k)vl+kuwZi8PJb?tsHa+ugW|)S_yVs;ud^&vQa6a2B=OX~^}f)>I@sMy=9U zte+8IU}p@%*n16IpLI)qjbFNNRG?NdY@9EPzRVnppGAHkng`g)kc{9+@}Vc=uI~*K zY@%SCs62DB_j47Yh1=13gOUj~^PLn!C=-`+-l;AqIF!7d(S$mL)~WFdd~x6^1CE~7ntG*E3IAt03ZNK zL_t(=D%CpO0l$Y@VeOIs}V1bIB&T0fS=g^cL*@z z$O~#fK9}a=dFj0&5dx(UZnj$;jVv`4+^{p67QSFrxOvI>D`LcEqD2k9CSQ>Yi^5e= zEnRuraQJo3zt6X8k58GscK(+ZdssL3a%bPb95QXR8@czQH=(xQaG~P&?U18fo)am# z8MB$fh4DasVLDOxuW9qLxWng}2%m(Wvdx`IgFQ-(@m7M3DDeB-J#`D@;!SH&?N89O zxARq~oYYGu7iFA|Jt8eIxLf;k>K%m?G;@=VZ?*q!yzJ>l z047MB+a#*)V_PTO18z)?GJF9dT!VaZwrKpl9)*|+3Q*g)|amL z1tgog5o4-FJMJ2<5V?ai7PbdmGWxs&^rK$64o)6)?;arGRSnpj+|@l1%G&Kn(3@9u z&cb=%@+CTgvM;a`MB1Nn@q6YAX5x#Sf0IjbGa-^w3TCton>Ch*fhdJX;;}VfmhRe{ zuL~E?Wp9s0dS~oIoNQkhGctp8gs z4U_kb|I{GyYLhNY^SqEU@wU%F*jGQJuI|ryPyQ*#w@j3l9y8SX)hHr#a3qgWnqX(> z1Vzeo=1=(`9DQ3hYhpu6@I`KJoWBk{2zAR(;*di%)_|RpRTJ{`ZQz4jPX3QY^2 zMn2TYtuau=xubyYQs^#aII!^Nu=KgCkU9*J-9>E0(tT4U{0h#t4LFB$=y#dd@AU3I zP&<}Ld&23C_|xcwlSK>8OlnkCKIFpU4S=s)4rMV&zwzDbp}kD@4($WVB_F-y{XF|G zUH3GHx$)me9tJ|-LngB|m(G7|n0!L|it~;?!fU51K3S^T%j1~351)p?_7C1pl8%Tk zITu$>-JP&zokZ1rU(|d$$Qi(~N5mJrRrFy<|B~XY-+OZTb)F^Xht7wg&yotle77uJ z{dvS63;)=ym&Bk{D24cwpdSUo)?YT7@ty3^J|%k7_a0GIZNAaG_@l3v#K5MbxX=Gh|2UOkOF#6gTMrJfa;!J8~l^z8GigX*|!i7_o6~FIIV$T9T;-P_~hX?iWtD<|2 zxMa(1)k+@|V7to4vh5C-hZd7g#8mdu$=)l1y4TlDVdcz$&8~=kTiiU7^*-1@=uB_F z3{#d@KKm|n(YjjUgilZe$CoH@VNIaxXr327yrNcgXoOM*x5IuwWlOuHY8aj>Hk5>y z;mSr`^5%u}?knPaM6QIL6Xu-V@V9FInE4{Fmjepiz;`7+C;YDPN#I;;(2K&Qvvk&Y z_L3-t7$}9dA-o^_uYx2s53$E_Fzr-XYPb5g;iCi7KwJE%?bm+feUe$_gBifFm}0lX zrb;FnZSbaUT;8nk4VpTo>Xs3A?eW0g8r=d9-<{l(J?t5YzGcLbaB}TVx^}`#ifD=! z%oXUjc}Jq97%XAo9MQ)U2Ir@Irxy42|MG;QpO{Xxx%Am>&hTV4uBpyCHS&$C424`qg66VNt{jFK@Gv*=UHrG4 z!;yH~Ccnwuw`J22L;psS?v9RDQE~Ue!;1DsvO(6veJN)juX19x9``oZ-c0%{hrPbQ9bdr-dv;Gh7rSZf zc{NDl%=o7GWs#SDzj8115W=0Ag7JRf^USkCmpxv}3gmv9c&D!eQXwpJqdF2)`5#qqE zLQ~^i<9*|?DHyRu7lem(XmK|M`)-AC98SJBfIS7dqt+mEcCF0eiWBh!Hr{V|FD$?< zzIHixx8WOb)VAfj>S`;%#GoRE77lSwuM>yP51z4|0PCgf7U{XrL=Hz%i?5v`LSJ6Y z3U1wPA-LeLMbAx^`B@o3$*CCB2x^yu(kvp8O%-F8%1&S?_#} zIgQBB4JlKChsHFaw|8E&ovy?+5pbmRvV@KIK6H~c@--?2-=HJd?BIoW*a^+gC@-+5 zuB;y$=bC?0`fUMk-5fLJ(#x=VY&s@?75H($AWX3f?93H=X^L+%Kn_Ww(brxo92~pL zhUpwU!L<;8`VyC81(C8FR*{2E)Z=$|_cbH^%XXb_mGO>$#}I5MkoG*QclxQZoa-^* z9`Ek^RvZeNqG1=m8}CzNfQjnP>;il>5?8~JxBU^jo~xCP{PA@-R`=4I=dR-g%ts@N z)d%M1(uMSd%`bBMSw?gjHs18FFmeif68MkbF+D}c?(042=vkUz##|&wxTNgW!`1K0 z(q0md=$F%QwiYo{>71bSF8xL4ZeofttP349#Qu(`P_fr+_ z#o7C!lY3{s3$zMqk zPov?aK3VcqlZfYG(=1_)@|A}dg^SmOM>~T0uo7LOLlGj)UlJCSOOLmjEOF=599MQ; z`8E+V2JyV`qkwndkPoUL$9}94KsliC9t}q1xkK*pAswUdI5B3}vQOZXM59gd*-c?> z#4Uve|H?fL?m<8w?JzaI^bK9wu8KW@H>l5TsrwFhpnxVcOHS2(3)yMpsNazI*13iY z@Zwe_zwd>Y4rfxLMbabF7n$cyS>YX;z>4m7>TdZw@teGST|l$P;=(u)=i;FejnKNC zsTRHb-S72}8~&x^FaMSJ(`~2wCu@Q=oaNAv(jYqxBAdeOwNuvp#=gE+b)xOq0SY$9 zPUUNl8@yBZN9Do;vzTiV9}pw`8wi_~`&xTOCGUm^;P`doi_w)2urzb@#mn^n7rj!nn&o``tS{wgOD!6ZIBvq$yf*VPTTj zOAoE;VlDw+q1(cR({IgFI27G@o<6R~h!XU;yurud3KBz~2;@4Ue5k?~33 zN5P}XCfF%D!D@{pP8Klvcl_;V{A$V5C2xPD_|BL`9HcpCD`rY-nhvVL6d>Z)E*IX_ zt(3=h(Shi9Poje$rfyT>1?f4b*c5L15t`Az9pcR8*XZG>3ep+gJ+~#{m~ZZ((;ZUn zu*&{*wE+``Wo9CmlHDXP$Yk}MBDuoPh5PZiPopuvQ5=rCcObm>Wa@n_+_wfsX9aMC zUNeCSX+^9x$-)|G<9>?@?w#17**A66DY7{dkBmpQ`8v1%cgI=s2c2G){D-pXeD=2S zQQ(u{ZBTfma502*y1Gx~ZWYOB{))f&XMeud<~f>R54YJC!m%T_mzt?u*NwGR(^SoB zF-yUhaNO5j-ajE*>ep(6n)w39rgPJxZ0}ihK7C$zTa;^b(r?5yG!lolbgy{0gQpID zsh{33s}6QVy5?BsqzyWjfJkg$JZR*u0<(@n?GYYtoYL8`>EJqxhQ)f# z@vujC{n6?+A+BZJtY%5b`!pYS=`Is|AFvw_WTfWqSLk;*vVI)Wn_4lG5}tZ~x2EFv zdi?_5t5aZ@GyoZnd_nq#ZpGJyZ!+H&L=4PYaVCBc`B9(=#CQ9^FvWdyr^g2|(*%t- zpGaTPUD16(`_q~_2i}*2oGLmRB`kRZOZ=7V?0VbngKWu>aM=Mw00k^kI?lyJ<=WK@ z=Ju`oqQ1XjgB_1Hh_hY3;Fzes%M=_Ba+j34Eq%SW+G!_bbp+Bev{JQGga_1Uv+>Eo zy|_w;ci7-FJS6;GW*SfX;6&Kxn7vPTzwMv(y%y>Z5y@NWSgla%(|(95xZk>Nobxr7 zD>sam_&_Q*x?l0#Ex*Y;D|z87mqFXmykmT5d~C!>^S;}eXz)DJX!s87-n;c~66Wy( z=_%cF(l?49fSe*t3F2H$OYXeq^8k%f^5(Kox*92mJUFDc;^VH$s&*~dDZJoVnkl_5 zF^XD$bL+Zt-okyQniB@lxv$0&Ye&>wzV5&%!>sk<>J1p^uBvWaVq!$p^$`*wkDg$1 z3$*AfmbKiaAZBTf0qE*F!c~7H*yM zGP+*cF@&J;a;?`({!6#+d=g|6&8z?71LLDe(-7W8N3MY5thc>CnAp0EM_;#NFTx1# zIIi>(JHjpmE=k86zxIjpn$S^SbFWBeG_MNa&Qe!u|tDY!v=?9Obz487g*(l$=GH!fjl>bllMv?^=2>ajOM zL>qilwbIKxK*{pWr^no3`;D;}o zKj4Trv&zAfE3@>u>|3p_El-UpGnNHoN!Wj$h`ReS>B^&^)Ov0WF&5wM+p%wm8ouCZ z;;Hk$ww%6$o%fwo@VX(tC$uy8cYe8T7y9L;ZG3x}2G|3>1&8nsdeNJZnxNJ(9gCMW zo?VwjZ;AUgoGkVSJq%(m+$Hs!sR0Qx7O=wm*8Wmu={3A-;4hs2y(5a^C5nkkAk7rF zwVVfL_g$^F1rqm2i91?oc)5mSb9C#+QmM8}9~rf&s5^tXD7S|TcWguq>KPw>8D|mf zQ)T#XCq(WU)Ioypp>Qr};*#n+MP3dst1)b%yzsED755u7V<$(4rP=+Sf64s&$jL>& z_kYg>xu1o)y0$djK6vQNA%H!^u4Cn&LD&O)1Mc2YlVC5b{i>Au{s6~~I8k1daOJ#n zLw)Nsb;JD{I*)yU8V*UGVU2hAlJbh&k(^uSS&>sf6RE+1N=uPwD3V2C7;5XTZk1t1 zx%zvSS+6JiB$H8!NxgKtsC>AwH&nOHO_6k_HqzmJpxUrVmuUJIgDoNL7V3~txSMy4 zwDrFD9Qv^3^lfKqhqr=ioa3-9s~TF^cDx^2!dhv=!VMP@F3+z>ygTd(fe#QR*ayJ?0)|95?^)4sRvegEPylW7V?)!`<1Y@t<2{U&lU zXtYVpuG~{m_=D_mT@@(zTM?Jk=TP`dx?57_MSV|fVF7{wiQLHQJ~6`^E0j@v1H;Da z9#te_9mP=9>3!nqcv)+-CU>G?H&de`nXSxr4cS7jDU#+=D-FB#5k#Y|l;*~%_`Pg; zM~E{H6%Vpt5J6i^H{>TC%E|+EzBl#YV~sd4USH_8&!9xY6uV`nHc&RsM6Thz-#5<6 z{b+@w0cPQbBAegxJo7gV?N9m+)93EEOS@Vo?OwyQT?X_TX-kW$)_W>!rR->QeT~8{ z?$9dzf#S@?9F~2N2f~eH@c**+CQFheSGL$WYGyvgkaJcQ3g`wJ{a*K@JPNx-o(MmH zJ1+SF`~rRpH{5f>H9vq0Zn))wo zp^_5mZmy=|)jR9GcjBGl#f{1Z-3hd&Z6Lu5Gt<3zFWiOr_y}{V9gQ#tSD+lxO#n&_ zSjY4qtYIOTehZkK<7gP}>-RIv_{9E7R`L=~xS=Om2?+Yr)@^5cg&c@L6>Yj9CV~`D zFR?T<4kJnp-8dcDT_6qJMc2H(yRdEPX!oF!kY$pnc3>#u6Vo6gGtdERzsn=g;lx;? z*{Yf_23D#N1NO_OunJ+U4tA@?_{$QbU&xKR5S0NmnE8KTheYgPqr_ZMiCT2WCws*O zNwjzR3r0yaYUlBI6tu>iB_6usXiR_Mi{B8x2DU3KmoY=kgVcZk3-}T(X^t3x1&Wdp zL_E*{D?Jflv)GZ)c3sEDz8JwkSgD)U|LoIDSBsTQ6dQNnAmJ7(BIb%MO zXE+n0DhQVnKegcszhr2C0Ztvmy`aztlsKboYDC9# zuxD`|xg*n+opax9KPTW;rw*;fL{1^d!;shtkrCeH?IbR1kDKVXy#s+=NA^#Zeu5n_ zk4{9rx4JqGZXh}gJx0JoBs_8`hQPhBhD;1<`uOSw?FJQ5{$e*idk^K^Aa@4>%H@+# za7~U}OigVd4{abXw6G1}sV0Q%qBjUyiw)t$CA2VpXPYIeQ=*C~aR^}I_(s;Mb%C_e z4dB3m!CKiC?Y*H^*@u0A2~|kvc?z65y>uKx185Xs2SZ>$x@9XXMSps%@4#r z$aRz{@h!d5D!!c903}(#O^7?5nFG`$SSjHu_1XJjRY-0%q5gryDbLH0m3F-u;5uOx&OHf$Irh6k}08=;0;m`7`B z4F)*Ja)|OZPfiP*Q>Tuvh!cmh#BVp48(QGr^dL2;N>s0j7eL`KrUC~!j2@1v_B6@C zwTs{oEWi>f=}MuB(HKK!?BGW$6D*So_pW>~Lq_@41ERm1)#+f88sb2UA0CZJ#}i-# zZW8|vv4BZ<5Vyh*$yKP^_1&hr7NdrPOe_dGm{tch=#F>}UL81PO(p#Vd&Z`RXbw>j z6I2DZjhrV6$w3sBVl7M}BgN(eiW227Vo2~P6?dnsfGg9|*3=q)1$&T|FaQRTA(oo~ zGsE1t0zB{_t);cRPj}l3w2~(o)c8YIBR70OoI9KW=LJqEkgg&u*_$0hpy92sk{VPe z#IFnd!XX3@EW+(N0>Kq%NK^{!k{f~pU_jR;VE`CJhk{6H8Pw+JnPAyjZ+1~pq$$~n zO9#G_hch&Wj(GKYf&8UX1PkDlwh#<1xN{V872{R4Gr$%oUUY!c;ibUBBVON2NVtQh zR5uQw*L@&IMf>e_=qb@CcJ;NS_CK!I|?(bzFWo;oK^90mkS z!I36w;}XQ2F5$ShIB}9^4Qx@pqW-4jyD&(hdI9$)iJ2wfM8_CI52B}e0kDu#&jFH* z0^oCPTypCGL_)pKJ9otsc4xj*<|UPXZe3A!ODuJuB+LXt7<|pf&{C)A+R2E3P|yvC zrLYb3ae<77?H&FD`3MS|4iThljD77i^6r&fA=mwwU4M;6j5x1uQUTPW5~6|&{xi;I z2F2{)t|YeN!tizlTS_g9ui;Kpu%{MiVV>B)ZsoU>A}$Y9)P*&UfigyVXa&O zBbESY4z8jb!JWD4JVjSLbtstF>o%=|CwwP_0Xz3~C2;}@=HLjtAm0~pIjXNvzKzVI zfqBUZw1ayLwx3$76)5Wxtv|W=gam7twbV(?}bS-prNQg2-6qIxqs9 zfGeI+K*&7Ea51`qUn1sTwWhZwrcR{@@f}9#`pV!aCx6+`88_9>yrbT|wL4YrSh(J7 zPy*BtJS8r}g)X6htI(`bE-aT8JC98_3eG zl&}J*XcgSh6=S+FaO!mKcn*vl3I~;fMRkmj4A_Zf=q7mJ`VtqaWKq$EumWx=2DQQP zD-`b$j*F66vDX=OXEa~5a2k6mu+v$0R*}>Vhwh`94m#~!#`I?Ap|94zFXkHt32qDm zCmdC1TG8SOa20Bg@qM_=E@=}2Lnp!-!8?7;I!JJ`%SL6ZkbTM~)(HYgcApGME3d}L zPndB)ykYqpu?EI5AaU(ghJ0z>pc~i>0!%SF=7YEbmWCB#uA!x@frsc|A!h%Os#uL# zgxH*?4yVL~7=)Vwu4j@=P?K4L`U&iAJNpU64eMm&661zjv<_tm)ceZ8y^B!QHPW5o zo*UUIy@EVnsQ!Sn+nvred8axKz8*f<-81bWfIUfG9FxQ;F^sp~f}Mc{4N6%|L2cni z-NlOJ=ai$T0M?znadfC@UHg&jj5?ts?mf>+5tvihTqcru>4rmYw<^bkBL)J&nPDlM zI&C~x&`PXR)~Xw!mONUIU=IP|Msy>V69(NhCd8S;gdh=8?3s@XColP3Ap;pBnXN(s zvcPy`@3cF59A_Su$un^Y3M6k8os~*YsJ@zeiY}COq=#~)&W?SriVE!He zxYU_T9Fl6@B!!C=Asdtl zT6R}oIs%*xT6?&vtEsGV0;ru;$UTF4NJ_6I<&}p$z#d?0XbUOC-~fg1tXaXmAqomY zDR)A<7ryiD@6ZZXgp^erOn7oeoVXZ*dcv5EApo{ONxy_%v@|dCIly-d3qVP8+AUtc z(cKOvn$>Mvx4{wCy6GXrMl_vJidhA)lqA|ZX6NhI?Qlc8E$9&A4gej+N&w2k!=1rk z$5`dee^+O}vkcqqtO|h`@fIsg=mfMNV)_kN(Ux*+0fcRLFVybMvpYunkQ}6HWrOdL zG6WVme-;KVf$&g6TMBF15^CWk69Bdm*3^g>hJW(DLN#I+Y{gE20nMQra&StXQD-h> zR}>t(LmG;qlYNqO+fkCJ^XOh7d*a`_MeHEo)QF;XC!V(uUDI4l001BWNklq-`+0t@>fNlX;13a$fSlqLkmvfTd8cQI_fzy zZVkv`qDL9KrjO%W-GNgc)mwWTb_mF+i5$1(xM2^xcE$=0;fQg#3I+xBpd%`^#^Hm| z0FG1*4)fG)slOeh7?-`DzuP6^DN~nf+7jBrR?r4sFrhIlfwfoyTVNqBg@vJkw%RnP z4OXJ4SWQ^I=3qjcI)q%WBZsoneI!Mhg-z^1pVID9>=KsUrOH4s`3rVH)|+-ije>AM zD~T(=nd4?&PiMNB>w2cyomOl8cmM5h@be-q9kWg=jvFjfP_H#O5mbv4!ZMzr=V3DT zJW=4D<3omD_FTM!S5(g~^+GrjYY~skzLPAoUF{M24v|XaD1H%z0ZEj=fXx<_;@xou zRo_P1ibFvw@D)_2uzWm*FP#FGfYKzp+o83zrnb;lwxVy~g$HS249<;fU?nVL93|gq zZ7_fpU4`Kv$K3v+bLIjZ53E9J zpbgy#I)A$zvY?$tkc5MHs$!j8CM5AHLB!9FPPUs!3xpn4kre5Pb?k9OB=Zp>b8Bx} zZ92IU1LFz(rB$s^xh{SrR^UcBgKP~MQ80z;mt4$`NGDlPR}dBjH*KH|Y%SG5O%HgF zJ78|Cg@v#Z)*%tDWZX(Er~4vn0xD4K%>qxbuMZO<vT)RN680(8B8Iv9`1#EVQHMnH!wp8ZleNrN|rFP9J3n1 zoj9CQW8#FY0Oyi@Ix}ZykrczU^L!aQs-$j?{ozLbIi9qxevP8n+o84-$(qb7966#hE$`WP4vP3yP4oN#u&Z!JK?w~=AhLHxIcab&nz{Y?53)63gCh&bZ)j-3oM?f5DZN~Fz9OSO0H z{j$EgMj&ms_gbv9T5GjN-KcHhTm+*)+CB+Mhs8PE%nftH`=ZzSA%$Fq)2P)}Q7~Kp zm4KR@0ByF-o#>iloo<7Z$6}gz=5>;=Uhr<{7PEt=UZgmsO=$CURdXb{(u58jCafYu~n4c-d(rm3)!yc$F6AhHuG6nN*W;TLAlb*6hG2W3Y1hDx} zsAm#b5+nFK6@f`o2onO#jlXU2i$V=BH%^WB!b%Tov<|MqV8ZgH8%*3VSZ#;Im>44Z zohbwAK0s)o+XKt5elP=A;SfWiIEvoYLae%nsxn%?Mo9!cB7 z65Fj-8?`Nfp0u5oyQBE=O55tY3%&g-#H|cqFOni!Oenw&fGx0*1ZWdhKpo?~TX^Ji zM*(^jrgk{{l-@X;NOm5Ti86L{t_RcbwXy-?CM6>b3H7ySO#_*o2^1hrcWcJ zP9i*fm_OG+oyVVlyz+meQtW{w38+$V(H&4LaVbhMz5((zNQ_Q~H4-1C43%^imn5U5 zH5XB)H9T4PCxC<70 z6><)T2QbtjQ)Lx^L4}K{Akh&@2JMbJu3TBr59Af3C4*n03_;NogNU5wCmB##a>uyf zOgiDnXyll^?D(!o=ORsGGWbWQW}<~!$wM{K(zZfP)xmQ<@>>Q;^inIffR}2}LvwK{ zG}1!N+z1XT+JI2>#^fp7iQ@^YN#M1Cn3gpn=+0V_03m6p*`X9NJrBT@5wwr?q&nhiN;VZ$4b=^4hL$bidGM8&fm1En2UT@S`jfX?qN*V0Z6x(8TvxP9lS-elLPh z(oMVL>Z4xU0kynwuIY7iafPdyT}*X7x8)j5qqars(YC!BDP)@&47d+2+y#^zCUPv5 z7e4aSQTW@daI^>jW_Ma#ZT+90iMnSLb9Gz2Hu0vY?$JgL z*5pxeOcgd;U0m7OFAF5!t{sc`2{A=)*fGFrxq=~>XTaV&xosK4PQ+zTss$vSX*gB6 z1PYe$*U%ESfeZ%33m9o`55@(s5EjDP&=4!x)~t;<5Ki7`F7hPhIVb0g{@SO+DK!LG zD5|%8(0eI{MeG)dyJ#kWMt9)S^b+FPF@$vZzHd>H@eKD`)IwelwwU^h>E^pDyPVo| zuH_0HY3uab&ao^ps5q$dz4|^l|;cA5+9>`{V&#E5k-5Gb*^$`#P4rG>bV?!{Z-L4K>c5e&}3k)SNNeHL^h zaO4K3)QRH>bn1BGP*N-)C$9NYavJ`#NOFv~k%|FTM%8vXDR>H; zJDfU=!7)kCL_x6R|62Q6-V!{P0AXXWa2lW(&;_TqQx{zTDhF2t zB_v@IX(37-Gw!;BDRc}Ua;ZY8MjTZ)iB3&S^!D0huVMu{s)cWGhufQWI?E zoxN%yFFRsWSvOEckgDJYC{CO^VRe!&B#lB)s&MDRM@SAFR+Pd*w;gRxA_9<*Ou{+c zh?Tgc@LNP!U6UQ~r>ip9qBY#;!*qQ)wW}LlT-)qctBvXgoz#X9yr6(9IND2f{iPLR zv(@!nKl^zStuw<59B@M1z&mIIZltItJtcf+?95P9Ph$4D+1*|<4u@s=i-M<)!ANof zRLPAbY<^6O=QEKqP$FfWBy~V1s`X5il8QV7cXa*d@jL)Jp#qF*u(b7!|2Vaekwdci zJ+@mV4k;sT#g~YXK3Q6i5101szc1k+Q_m2AP>WM1=Zg}og|#TcwkV?*lpE+e)CB;^ ztq(m@6~QQz_u`h8?mtwZ>kNhh7;iIJN5FbnOu3(1a-GckPWI{4Q3Avc%;IQB2C2=( z`!KhfU$+<2?RQspIo0*tqRrp;+Q?ubiE5;0NP40Oj#2-fOkP0TEJ?zMg@cCFMqCIo zX^H&&1BZN7_f7kZeE1~v@DPG*S%4V;gcWh*H6|z%Km=xYyR;2PG`3` zVo>43rB@TPu4eEOT$Q9DU8Tljunox#S5Si}ImX%@J;}@!%D}4=$B;2t(R5VyS|1?8 ze4Sj@yBnwbF6_j-STYTH@Op?KXfU0!!*Jcmu??Bf#QIi3jdpgR|GBlrR^v4JW;o+ufwK4#PCME2w++v5*nNA3qYbW`@;dmW#|gpcBi zWbTli+~3pLcnst~RB(>L_&?M2w7s4Ogp^rGcBQtSw3P{_b3i$|TRQl4cO4!**^i-z z!#z8M-ELI<4jsJp*l3T|dF*UBIsEAR&^gdV^AQtfNHuz3O$v?P|0r;F9S7xl(x$E2 zYrD8^FRtzCS{K(gzt?IbAL!aHX$xN4J4^Ni);R>V!?iwa=h#Aj8gD((WVUJgO? ziN3Hq&Am2rU^=XZ-MJryxvcB9#fK|>_3sqog%cg#5D0}Rk|C?|CiuT^qH?OkGhxTs zzZl)RI(8|cp5r0FXjeY#B?4EyN7o`C+N?L7APlbt>>$OQ4u;p5c) z7KmO^Vq$10wHyNl+z?nvwMc_)P~f3a z@X}n@*PDx}K3v(w)Mj_uY&$hlN7`5xa*m@P3Qmbjh?OW-V$qRvYl5M6~F#(kq>ly-Rm0W8T<*5cM~ueZ~gK2Gi9G+0O?Lh4By zzg!E+pJPBp*Pa-$|$NX&xqpJfS0{BY9tT>e$dW{Zn!JNpZl-)8Gs*%La!SHucf zN#~%|V00MYlP(hUkh`FkYV`_&@PGNg{qKH0f!FYd-kT5_|zzv9%X=9Xv z^1TC>UD)ZhAn7YI+s>YX1kO1;m!D(_Aw#Zi4E3J!BT+f$Fk~@cEpQ36f-RGCZzxHZ z6+h&cn!~4faVph8_w^a9e#*s>vs?MOWf-XEv;fBm;nWCN88(u(kXL|;9eqc9D;aZ= zw!CM#f@YN|6Pum^>nC0oD+M=)_`SPO2LQx~A;f{!&>9q|nkZ7;?B0BMG2Qi1Fb$=W z5yZ%rJpeQ9GIipBOnuxB<8YYlD~6gm{R-&4KaqOStgWs~3pQnJz$B z^neb(LVO0SpLlV^$14Gr{K47X-32_xbPgNh!@ih<1d zD}c|W@F!iu#ptUWsYU0TppMBFREj0tfQ<(hf<#*>Vzof}Qpkc>g6CbpQR#AdL|PCT z+EtISet+B6zhe0u)9fFn_dmW;7Pz`G-XZx=b&AixE)_Z%?O% z9KNC_fBjx9?@so9q3N|TK|&&KB19G(QKZFSt_BEdK^IY4tQmcQyFL`XW|E%`oHzi7 z;E=2UU-nbVp)*&F>0o0Hp>VGzvx9+S@8)gp)xmC^yo;mj$YZS?wy-zmd`5ep52t)| zR8LIdaP{`eVdI~(L^ybb2lWqkL8x0?-so*_QSNV;O5&IW(NshT18ab7;LZ^57^gq% zBNa)2E5RbQ(m-4I2Vf0b%P;9w9vLXI`6%0}C(sWMUf9Fg$x*7kr~2YZ&+KZ>gN z!0U+?c+ktyE*@(kfBo@I-Jj{4E?D+-uZfT08jM( z^r)U#1fncBy)iDJvpB=tS{7Klw=k3amGKRy4-?#mvc>RYB@<*B4BP~0J*`3yNjm|

> z>$&_ZmCaV)UD>aHUclvHpAsuNa-bC>@}oxu{n}yRIQO~}?uCuGj-jVSup%ZQ)^*G( zLQp1zRK;te6&ia6tS_JlX83reUlS0zd!hkziJk)#L~+h~uIjg*Q^P5>kvu%<3u#O^ zvI=olAd+5YU;}Q1*4V(d@QvU>s&da5>&qz4@xzr7COXC-jVW5K0iqc=WbwxN7Ut*; zZU~lSfr?H5O=iGW)(S1v%5Q`Q6bgs3L@}n$9me30IK?wy{q#$?5_~l?)a0OJvDllJ zCoG4wMi??A|8pKRng<$LEwr*ms-Z>>98|)D)mv)lSOMq6TVhhg)Qy~ogJ;0{B8nrf z=d!u@HIP!+#-J{O+<>PoZW8mg5NkDCse$tYR$_?pWpg03Z zfKyQDKQr%NPFbvUHS=G;?{1O;DS84aOrRS8z)H9?*I@L5`qtr$d_}x;C<*6S4CT({ zPU*%#5%gYSh+FFrGXWa#ObWl45^vo9b&TE8g^iI2^B6feV*}jFHsV(W{zJ(Zg^HJO zWATI>!#PC{zku{wM4ea&z~vz_g)MpptS_aMB|cp0Z=-v03Jx6XI|Rm#08BDf1LDsM zofa7VJEDR@BuAqxNtFmn;S_40eYCw`JjAo3-81@4^yw&7ltclQ z#1MK<77i$+Iwh;|LP$Ll0u&`nb|1o9LcCnTXP@^ksqp&NxKI}OB^d`nvBDTAr~=vo zR|Xd{;l`?oGgAs`mF4DBkE@uf?*i7poMGXK@(1hmU&282r&FB6><$mJB)0`m^)Bcz zi4g^`U}NB|mYgjk>aAkc?oD^La0A!jtBF1CKT5Jt! zA(xVggs>th_-xAG6g`+@D}~g+-(d6i?zj!LGt}?>J*=Zgz_Ba2gG%lNu(t|bk59Cw zM^^1IGw|WTI2`TaI6tR62BX@2sDt{49uS8!I$Zqx`O%WkiwDEYy7(})orb971#g@n zDh*0tEqIe{Q)@ByLmU*90(?g|(s-}X-*Np9T>lQ6P3D@cgFQGrb4QPC{*c+Kf?2EB@gGAK7>%pmp9Vdcv1vP7Gh%~hRh;|PL0u_ zU8}kl-rk}7hGZX+FF;D5g;2ctLjn%pZdHDI1^M_w99GU3NXKCG>9shz zE{}DM#~(h?#UZdBt$D1Pb1(ylwvBn`O(+YDhCG?zWbDpQ`2G!>7Jv5}zpe2ocsNK+ujSnlx8`OdWq0NZjZWKQjK!f@tym)2$omCZ_dp7B0C6YD^q)j2-vm-`Bj)oWx z_~O*PJmb69{Mmbc^M zOe->Uj_?JG5wQ@i45#-jUMhpC9jSm$w!rq5B>t(bZ&Z|MTMUO^l6Qj(p~Tha!U#xZ zP+>IW`H7p1ael(LFZuObe)CoF{xt?Ksfi;8URy}|E&Be16Jg<(SuP>+6EZEa4$}Hc8afF^JnkfS8w?CRe@JT1-UWkXkKavlW%|; z5aA-fO9(E;I1dND&mtt0qqncx(+S%ae{6B>9C396+)D3aXc(r9mXgR46S9^nG^W8q zXgvpBZU8kMNF_R0((cf1v=RTIU9UB$2uJs;0)OUyQh?QywpMk6*6)zE@rdWAeD{)X zUb%1I@%?Le7ChHn2{#l%609u*wj00{(>=z+8lb9E+$6O?Pe#&rNqqmBug*E0X*B%9 zFT*Fw?SrN>y?fX3S%|z=V9dwFE$D!fcn~~>0y0=F7|g?#fFQS{@k>|I=+NVg001BWNkl0p|x=OoUdsKAY|^qSOg1v2dd z0=<_fDhXVv^%xXzXT-lPP=e#5QsEPtJ?_E;cH!fw`s#2MbnNw`UF-s1L`aIlf8D?= zs*y$mzIuTdC+@`=zIp4udB?BbxL4=ID~Af;7>hLa2DsynV$WU_J#6M;Cc-DR2NihbydO8Zh^PmvC0hB5KBUEpi^TUoe&esfmL_KJ|EvvT=KZi?MOLn zQ`zUoRICR*W(vukv<*s(hCCZ_GUkgDzInxO-te3E?)_^wc?q356eT$XBsBD`Oyn9K zDFj`B9@po|3#1?!zY>~aho}P5XUcpQOd2V~bHymov@oG#T-!{mz$k zuNtzS^3~=+Q;&VnqdPSyaWOR*RP?C{yLVO6Ia(nMQQu>5FZxB7&zGQFAr*kd^&tfe z9Tyvo0Ik7uKrD}J^Bx@J<>)-~Wzs>l!#4AheeKmqLffLQ)%qyNOh!CE#bm@6XZ-55 z``LSb^_E|~a(G1!oZNscA(I-amG{D`Pl|__v??A>b|Q0tDFO*MDa)D{za+Mtgp=qf zM*#AWp#|5CR=B>ki))+S>T0H&x!v69VJSyWMp1g$U!Ox5diwbj2l(i(hkFtJDurST zsZd@ZEDX@4e+TI>CRRd85Ai{2Jp6-PMbxmGek+DFf}~$cVvAOC4k`eXQB1)%f&P?v=daxTK13<9>MRl8kaI{y zKInd-)}t&is92U54GCpXb##RvTbw5df_DEthWkI&*WH6X%WLo@m=PG*3M;1|U7HK}Fd?*TRtFAyj#W5$OfdVz$H zbfi2K;MQZM`v+ZI`-|&18>UOSSu5|c59 zL%uk1FVFb?HNJW8-o0?|-;ggIhQUJOBu_Pxp{?- zt4!?eFgw*?9G9yYP!Dpy^H`c{P+%h%(10~i3oXz}9z{V(@?`Q<{xJb7BW;P-Hk1V> zW1dVn8S%vlU%%kjZ~3!#eE-IszH&GvONwI>Fq1X}*{(Zclu3;dAw{N|(KD8ek$e+t zo99|C%La ztoRs$zdIm0?WIV`CX8H=k};48`e5z~@@NfpBMDUj=OB zNNHo0QhIg|(Q%}7E~=G7&|oiYJXSWp*Y&MkOmQ{0>0I-B-QL@#286PFgtYy%%An-c z%(O8~im>c~qX_s*PHup93%E!UnkFQ*CYsVy`9pPdF30N=jk z+n4U!_k8!-oxKLn99#i65&(H>0|D(i40u;t9bpFvT+)c+pG-l)IA1CAqy<2@LIB`t zkbv5_@{0#eXS%r2<;>=Hn%!x3r`0B=sSYqS8U2(I(&v=bM&C{Gi?1X9(B&782YN!y z!2%pmwb8}pGLtt-@TS7Cpo)?p%rq=m&s6^Gu-OXuQb^kmC}z00wm(DKmXPF9Vf9;$Q?i{x zL`<Bi9OW;^<%kONbo* z!s#yy801KoSlJOVWR#j-wu6P5S^)4{k5?>L8;u4$JK!w2!~tzBO0YHrta%XA?wP;_nW8cA;6VtD(9&xkd)5g7 z4kLAxb0i>>02x|h>9-Lf`T0FAXLdc;-GlCzyK1B%{|FtM{?yCeJ+5x-{ktOVSEpZw zP-4{R9t_cJ??ph>H;HR@fI%J*tXA=g)lx6-_%n@f+wE4aWGNGE9f?{ZNXZIzcHXlu^BpqB9Dn9y&Q{yuoUh*&TFz%@3fYb*bq3>;Fmo4t$taY zUfSkCWv5J1G^KT-K^L?+D$n|q<X&N0D%=)P+o3G|Df_h`gXzMMej6=$WKRdSH8gDoaYbt zl?MM$TgFyW$MFkO(!|c)EoK%gK+y~kJ+y4F!u&zkbGx|FbY|1J&F*yfpiK?r#{WI3 zt=s7*RyJRYCl24y1$}I-KiNC7E_6^i?_zJjdz+VkJ$gL$2G(K9!?7Km%TcI3I+1+j zyI@_GxSR;$h{(uTMZy{^SsJ9s}j0;Pu3K9z&u$=&gg^P@_k3mV^gpqSPk;Sh1AAhSk71)vw)r_tJ<;Z4 z=i#wmvr;#@ytcDn7U+_J0Z4K`C%_Hl$b#GoZNOAc^1b6y1g^lDwJKjxUT-c#Izxj% zFKrvGbbF_ZYrB}~a%R`JdRXZ8khDEpNPZH<3?HWYH%gc#8A}LA4aQFJfH)u)!ouT4 z1D0{`6%ZRU^h%kj`U=Ha*?y?6=lVF)^v15{iWZV}W+C|^jsIUn5y}$ZO{L(?AA;6~ zJ0(Hd8^INjM=?MPlr!O*ty~phFNu+n6b43}kQ%s2`QQIf^=+__Fq)j7jsIUr=>@}D z8h2(AM24GybF3&2R%(TEYV>myC!=PpmSyMjGVscvRLcl#(&h6`*()LKcfB}t!0!7nW@{<_2zJhu=cb& zhZ^d~M*Or+k_S(Y%E^!I%Aro{Xn8`6e{3R8%a<>)z6_uSdt+Rd{T_ zp56;RTGKgvmN+)UA}E!rj*d0UV?)iye`t8g+dS;?sMd7v^`qn5Gu`Z2 zX+9I{JPpTt-C<`*9Z?p%noFjbt|Z#;Vz>|%h~Y#BYyd0-Le+bq%%bsx_TmZ(P)vF9 z*xEgDl0Kgz9Ku}QV1D!;u{`XI@NhghXsv7H=yUAOm%H5^LU#9c@0Cwth{Gv&`}&x< z<`A+E-#uK_!;bcrh#BbTP)mR)3tY{_TPUHPbhRnG>o9Ntu(c}lA_ksyS z2%XU#KOBd9{1_!VJmNU`H7`fo*~21-g$ErS1^T_84(lK7S_fZ`jyXRAdn=&Ff5k3L zJaH^P6aPI?p4f$>(@)Ru9yGo2>1JSzyNDvk!0M~bO zbOMhZ`p4=&@#OQLKLIPhPqRO`d=7ek;@3T3`s9#)07!j)onxi-x|&4~zoae*p8=Rg zE}rWOsG+IJ&m&e#B_lb)4v+O*zU*>wqyGL>f)EOj$0*)TC@OjY4?^2Yb*F9L4Y3u$ z`x&sl^x}xJz;{;)vB7f7Uoh-&2v?CHD_Th`g+}me05O~xqq|>X+l2kJa?j<9EM(@c! z9;M4j5((3U66k>mipJ^E_~XN>h+0054-zLoC8LAuzU!j%(}D>b6l$!@ci zJc?HB2JP0I*RY^k`4$4olErgcxi7qQNn+|oS9AQ+@9Zz%IWWa^Q5SE`VNA>*2utx! z1d3O#dP_EfNeuv31sl%_&GK2@@P(BCNg*NQrbgY!IZQ@;^OC>%*1dbxJFjm8_GX3oBaRJ-n;Br(rkHNzh!>edq-qMcgV1#%F z1`K%y81Mo;WB?LENDPq}z^J7bJpe%?5ZzT&alT7_84>&D7yC0XGk5b#MCLhFO;O(8D8Xg0<*(jEK z!xruGNJFb1E;o$U`Y^-9i=H@3S*v=T8cydJ-Sg#*egB>NcUN8~dpHPYVA_DAM;kLXj`A819mvcfuj8Nc7yi^}x1C9a zk73%!(9h~8t57nV+|}*7{Pr>IVvCiSee!g#o`B=k@h+=fLcn9yE?<3h7cuVS2uW~i zI5i>mlPr1jYhK;hr?>9*rTh5CKHj=_x6ZCjWgtq1z@>PhVuU0Sqb@K;=_I`gS431v z^%?S8#dPT|;xIGhk%Sj5{=~!4AMSO!(>sUT8fM&!v!~zVLJUu%z0*BTR9pD}RTlfd z4%d+XtZ6x&BWZhfVXvRtAOAt|_Jw`_)_r*EZeBUNFbWB07V~UGEaGJ{JSl^aq!fWD z$Z~=dznT<;VYtTB9uB}<3{D)NPd%K}ocv+0!-EcYIzOmCsBU=X^<6>zht`EWG6(nz zas30EPlk)oTw+4c9Dn#PSF-WHZkIE#n2_Ogrs?Qb*AETtDpNGY*OL$F7Y!h0~_szB0Q3ZjRs2T2Xq_=gjxy^eR< z-}~l49JuK8%J{Cvw%AM46p1QJE&+S+5?EP!^>DGOGYj0jSu8o?IO(;reDIp$6Jww>(Nt%v`laf9FlmaLi zqhp?eGnI=q#L|LRH0n=gO-N#$|__nxw0lN?# zF-Y*4{w5%#hnd+FfqstsCI>qSo)azU>fp=WxU z)oV}|EQ`(b2veQjcr$zZ)mCPeJl(y#m1UWkuH@-HWY?gO>^cSc-+L=dt}jL~`gH5w zy>Z2ju?ezT>O@!}k4)x=ED*`TeC*Cdi<2?kqhcoMq^3X>=w>5pv_G(a(DBY6?`aOQ zqb^##aD3Rn&bWn3$IfgkDhQBNLlrS+P+Fyy6AWWP3A!nCtSBl|hyB*`>Ynln2+6XY z#ULVUfVEUmt?Hkq;~wAIqVQ~WE&H1{F@u<;s>~Mp#^oN+<7Jiu{2IvS#{Wdx>|)D{ z8~fEe`;g3SZ(cdB(O@C5M2|vMk#eQ=84YqnBD6fE*`SadQ;;OSDUd`5n3p8A(9xfC zJZk^N2XVfowh$XV zW>5=!7rm#)n(lP6!-Ed@{&1(}LH?-iSF!hzQokSrvb@&Eg`!9rr+y)53}T6yEN(P$h=ipi9B zYRL8y+Zcjcrv)U-(m13A9AaOmS1$4Aq4T}y;N%|;I_z{KHQE{9 z!KLFeaY!{dqmor&uOxULBC9JQB&D7X)76DW4JS564O>TP6ps3W?e|>%t5p9*|3jmo zsc2+hyl#$^uVm2EccCq&w!&4Cw+cgFy_R*ZR`M9IJ_Z@fN>*R3_STjb5&?sS= z{;m6T>#m<0SB^qvmatJwR~A%}DU{s+$OE+q%cMkCBHW~*gq0jAV2T|*5G3=Nv7rB?7aoehyc?y% zIclSR!{&SH4^(fcZ>e5W+)(_++P&$&>W!)}b+Vs4AMWv;naSf^)UrGx6+XQ}zblYE zc6}qb{JvM7wffZPbf)X&oLygX`@+3@<38OMA8zgAt!-``TeBiDI+juWpbbj=kA~;u z(2?@POhAW3_%wbKh$KS7%Q~syaL{3|!<`Oy>W}gVZ5q80?>2A&FU_7gE?{R=qB2S- zBBK-g>BzPwBTgL9czDwpt>^^-AqDhSP#TqE^F5o7)E}vDsa{dOrnq8AaY_ds{RO+< z)U|a^=I|c-NpyG62NYXYMuG7!*I|meg`~lI)s;PeX1{tDq&D~QjlFy0?8;OJNH9Zk z3Bd%g8Wh;s-nTKJ>j-a2Az%X&9G5iZibW~!wUQbk|G!87AK?3vSr1terO6hoI2$32gC?T@jl~A$L&>i-oZU(iT!ypPFDH;ri0vzjhzq7PrqW_}XrsJ9d$x zB@Mv1rwz%^>K?gq4JV#bW}%efJtn!1SSqsevLX7umyU4mAC5X5b-2^9iA<76Lc)-9bO>1$2Y|psFIwRZ!A8`^#&C|(vDs3+WAh=A zwzpKTDJ}`A1_NpH4Kx`-N@xinpCFAt)xlO56|U=?3TQp`51tP{p%!O_52N_&?cK0q zA<79unaGF@V%69lEivN4=Wd=@Z; ztd|<=PyOM<{)<2Cb$+0I@b1jBPH##sHrUqm-t3ui0UHYz5*%#I3dY#5Bj67RBJf7Z z0vMD69fae1j>shK9EDLC^$WJ&Q{7U(r+!OyOZfs9H04b~NXH36ijy&u&NPK8pI-X( z^Tg%fk^X@<3c%3MqWyv^t)UlInA~48TVXW*dzvmf+Jc_#?A3Gkue`S49{KUMxO{d@(JxA0W1#T)z=_VI}%1b>o{&cXwYabu{c`tuZ-Eiae zs|pt^B$w#Yafww>0JuO$znK+K5?Q6(LXvutLI4e&O`Y`6f)_nhu!YjG3rZ=+<~{W% zHXo>7FGyR9Jo5ApKt|dO`oeO$f&c&@07*naR7unEnnIFh53{!d-P`3K749E&1(=mi z?8V7*;nY)BkqZ9v1gweizu|P2mj%)`8rp8%``4~`Zq$}wAttDb0vMQ2zG;~pG{|Yg zEiqi(7-~8KUqnKoZkJLL%e-`grav6CPd+5)qql=DT3!Y<(goa_U7B{r7ORtmq=IUO zkQn92#IsF+$qS#PHb)fMYA76~qu#Rl#O4$Ad+J-tR~c#R@I6|Rwr>L?a)h)jkUud@ zbbBs-T|Tqs-pt7vI?4Xj^PA3mJfN@tkB`_dNE=>+GbF!y=ia||AK%!=xAx|>|CcLLv)f!P7$9cPAZB!DOq$o%ct%M$bB4 z7hGEyTvQ&AhQ)jjxd1vJp@XN((y>+`Z5~-zk3y}wwKRMmoBV|f!wH_j@T*fawS!Ed^$Rhgb5i$r)cy_ zDcK{`DdtNMGJ0>lsp(HgI~@Gsiw+NJ9;Bl-XFY>Y73B`ym_2j4go{BNi3LMj5o{xd z8-%9BZhK)CWw!xK^`a4gCt64<;!2S^&=DZl#c{z=j{ zKAGyoN76iS3aL(s;)dr1=KBKI(Dl?0;!c>vYiJj?=x`gSP{hyeyk1n7ts5{A_YP&HUcgo6!K z#>OZdm1A>3{ejJgq(*v6`I6!SEF*1aI4{5`6aGiAnBIQ{Au*D+ffi5XCx6`QxYzMP`#brg$`dzU z-xX|krVH`fc;;*ewNYU;xWH>`ZXwC#fnh)U1ZRSCaB~w*Yy%h-lu#9H-mv+e>XyxS zk=L$XU_t5ACDL{nS3Tm4Z;;x)j*w{0}()Q+sy?^aKy>TCIU9gbY4mp=lNn*N_N+u04qjIBRHx73g z-%vJjT9K6stdb4z=|v|Re>ig3bGY;8z0UXYM{OHDhhNpWo#{DT8o{rvG7Iv5ksv96 zrd#p1OTRMRb{E3D8u)0ky>Jvzxrv3OdX;Q#S6Br-zC|5Acn*YRiz~LqA+ZKYPxIO{ zCaIr_3%RDs)nw8M%r{bb86&e zb;ksbz8~fE; z_wh|}{R+EuIAIn-a}8ZiO2`r2465o;h+`lf=`eSOp)wu$ppf*z_D=_A-<En(_+UOe`cZW_8HR_JsWZd+^%ROS!Sor#fw{{C9w){K^#~NiYn# znKs|qZDIe^dq?4MW?k5lqCCic@g>l8YC290T}N5y`kB3Z?LOSP+voP_)_uHn^^LQg z6s-*?CgY69(KLF2koa#!)?3LO0ulQqW?j+k$l_eupq)&JE_x$%hlaym$1ghE`STvm z{-Wg?{;-BEyfCid!r2y8P@00-zciHt*2yG)%X^Y;Jdg}+y&=!W*ccZUNn5?8{w^8+ z-%`J!dQI^xj>k8sqi=&kGh69Ql3It1Z&V}AxOFWmB(?SJq%CK&GHgBG_(Vbm&>UEp z4otf(YWu>h4fMv$aEAW(UVq`s&v$xtW8Z&g?_ax*x9;6b`|h>lI_z&t*)}tT<4HSR zMEDqwZ7U)G8(DErDwfa_WMfFocrOVW^(XDmI^O&J9j6E1@8!Y8N0z7=>?ll)VEO~Sq~NxjcTMW=e2)t zh2&RB8yT$R^U$0L5<{$!lF0Q_PeM?s$NH4yS%|_ z@X3c{ur=nAe}*Sk){+b+wDxv*mE(+o1GDK@zY`=aR8!f)fT2F^{{e`ziJO4Y8fcc6 zeEEm{fAe3Te%9vTtyd;I1C$q^ifnfzd=s!yLVs~U%9kx5n_)4h%w4<4@axPHY4SN(27pJ(ZhlBp8bPA-K#m`4*sI&rSV~dw%DyHK+=Zj z_FqDU%3xv($|M^FG}4@ms}Lkhix!fwMuD_#jLK0O^(FN?HXqr%qkcnuyJ8{fPjSA! zg|w|g&z#21u8fe)`8-Y9TB+xe#h&*quVGl`-vg3T4=3`BroVIY=M(G>1M+yU%Qy0W zF4CzVsrR<|==e=pCP0ts%z;&x3y8yls$AaF8E4;~bU5g=5B~r7v&tja(%XW~1-3yi zqh4DTltkgbLcu+9bi_-LBu1;qFql9by6D1B1sm9c1s@V)^M=jGXd$UzQ@y5oo>@qG zb2&HZ_6lmf4!u;P=`AC**A$|n}aU|4oR?`cu%Pe=9#j`#kw*ZD#ItnHa- zA=$zW`Yu^W3RD|KJdGqb@Y>l_q>wy|Nlc7(SfkpaqirB={GsSQ63>uiQrmQ{)}=P1S2F%vR3n9RZKbifWb={jd+K-8w^T1EUs7z-8Iqva zP8O0mug!c>mk4Qvbzkjy3y?=_cd5Goq|D z(4Qcx97{31Ja%OBMz5TG@i!h!$bJrc;=9E`L!C&~BCla5CkrPxB9jv`eB+gH>d*#=(wHPvg%XLAclP)I7@ zJ!_=xtGjtNS))V9urzBxpe&Zbte!_9X(|lnv$F!(`4CpI-GW1!T)D&${!W>&k-J|s zw`@XW9bQn$P{NDtD?N8O>zvffdw38pJ!;GU<$r80IZK&U9uh10x^!BoBehw&=B8I;w*0M>g-+yr+I0Nt@fI+$4wnHt_g0$r`^;KS5D{pg1npC#nTVs6cj6>4X zOTu)`Cetw`ZG(<#I*OJ0r@%T{>>Ow0J;^zw+1rJS#L%eC5K>-d!Pw=z<@iLAk?r?v z-+TU#poI1ujdmQ?QS!LvTrD?Y{ z-5&jXWD}7#uW;=O?1VqEeXZvd_fT4Q^5x0XLVqgLex5gik{U@+43Jbrhl0&>wjbEM zV{=RWn(_tJ4Q>lWsm*(QGX9TUTw_$9CT%NwF6UB*!ug`+^*_?$%6-o9*hI_E4DBrt z(xjiO70yOsnGJ_2D5szEbzR`RNNsZrnu~-?;k6%2&FYD_OL9EBmaHVm*xcx)vp<(N zb-fh_xcBz#So&d59rzH61PWtoY@slUpi+7p5mJDpfRKjck-o?G_@zSf=`~sW3P2E#2&ki#GpsCF@7qkBJ70{f^U->BVE2sN<5+N;>^)sN%ho9;k zDVi->U-j9V^=)>OLvVpBa3vVUg*N7ecX*+bw1@6g>Onw6NCi=iRBhP3kG%Hg71e91 z7ZlsL)dFejmZWVJJ>>k^6Y|dLsD|3668l#;UnbxczVkkd>A^^e;&iRD9bY4DgQAte zJ%LF-$Dd?iM1rKnV(0x|fuy{A|Hvn^YXID(r~GQkM_RN3S7HT-{>=GjPQT^!TaI67 zy#7>x3X^%?nH{~Fru5HCHpb>9oA=ajsNb-8MfH+u@J8|z(l)n{*rG#ELee@ynlT*x z{QU~Zt`SpXZPf?mhyhv1stlVBIL01FNRv~q`97X4$xvw8=ch>9D(z!hAd~rr%17R4 zMj2L>ZLR2Y}JS4YxrZGNt*4yRa)~1X;UVp21puFrcDIc0UO!OoHqc|FrTY5 zXlP|sT$r>mA2myYxGH}vA!)WzGtH!Cla|BNx0AM^QOhbZT#2hd_=fWr&OdVgiNg;% zdXC~}Zx5(V$4nHxo9q&{MumP$XSjm4RnUeymS@t6pO&(N3x@tZ~;jxV8mB&(mKI<3^X4hZBMn>ua$k8gIRWX3Aj_RTZPoH_9LV&Y5H)h$ht*x zx8#v+0-7u2b#;J4)6<#)Bw2|%`ZMPrIsM4-w;c95$-a2qK_ePsNDQEoRi+EGNZK3@ ziY$I_XlX!Tp;0G#I0I0Ei4qXb3a`#6o%JW#KkiE3PX*VN18Yw(9K)|AX?q$WEh<@W z)H2Q9H_{!}gacMaG5V|y%7Qhbzk=#AhCR0nS!!CjH&tFZ!R1$+cJkH*L%_%+(&Dfp z26XWq%^l4hryn@{mSd-r_zVxCiDy_WxNVlk1y-AC3u!3={gUO;a#G`ALWZLT0}p(F z2EyS~mz})}!7`jts5?t{l=9tTRKxTww~>vbolU`4AZf9=ADhZ=;b7(nX@P#!<-by& zGxRVVK^n)A7g!_5%J0(5G=#?S! z&=t@^Cu%F4abc>U38Zj2$oa3oD$-_?RT+NH0fjm8(Co<+^dl4|!Z4m&3qRK&HShDN z*_xudaa{+obf`ZZyPK(Pr?5L8#q1gm0#B(M8EJC`>S+(0f8_KN#~(PIb?o^f?!^gu zMo*J6ifr-pWfkE=wrIH{VZrHU??mt~30?&SREoZLUVIS;ahAdvmOg#&tT#?r?Q!vh zm9Zn3be0Oq-vc2%eN6%GQTmt{c${9$tsmi9x{p2v{fku}4@oO4Fg%&*Y%L;~VeC)o zDTaDxfkyNC9In7BO!$uGp7T!}f9Cjuj^gC`1=~wa3QiMfVr6Wxt%c}I0U;40@a95u z-lIOEzc`2tAJGvZ6~NTU&d^C6^w7&%WD(o7&f4JSX|8mXPzO#T3A^^-ONDa>^GWBg z4wAlVv{}m114$q7@QgY$rLAkLwGK&^t?_W#)Iws51({W^`6R3kaDoD-)Za>6fpZA@ zGv{A8{lxJ{j`uox?!~==8VR4GUj$_EEFE^L3}+OYfYGog$B4_>n>Ju0u#oyI=%NgR zgLHsnh{_xfi;vnvXGhFe;LN!c^f`g|;PI(JXxV#z@TKqC`P}@@QrluBzUl=#CvCG1 zgts}GCR_E@V=7PMzg$UeY2%YE`I>g)DYvBMrD;s!R7v>Ol{kkL_>S(#`KJg;hqF$e zFP^<((6*t45^AhARVfQxk%lseY-7jM2|1Tw%VklaJe;8!hpUt5j6S4YL1$T)UOMZY zHKMDc>DXe%CCp152=AjaR7?dNLZ^gDYT!Z0O7Ht`Mo6|+?DrsTv&xqzgJ;E-=xP=E zb9RU9lK@ErbPkX-Bahz_$d%fm;WoIZP`H1$7lsa)_eF>gtRPRQECfD65rGAIe+2!6UU!8 z^*Va{Svo)yrdH;WGZj!{8z>!Bio3*=*Pivx(mb33&Q&}mRcIXX0nR9j{hpMi%li1< zLtD@|G>1fY;&jL9Gsn*yk2-ihd+xC#fs>6XR>Ilco!KtqwWHN`+0S9K z8I5QeA!hJKG-#uwrVwqQk+o8v!ZQVZrlU7&3~%7fnUx_Nof6sg&`Uku<2&dn4WXdi z-G&HZ8{^Wr!k(KhjG7odx)^A*;(mx*-auSnf(uiD-%D*wChbhtN6ov2VLpP8(nO3~ zYNio*9Se?UAZxAeDFyu~L2({cEUvq9dI_WFTz;k__L`9@*b<4D5EVgZL@= zwe>)X4OpVuY$t8vN>G?c3ow^+7TW;f;Utbky=z#=5KvCWgY;Q6STCYR$YjE-H5z9g zTw!I>hN76{!aB+0h3|yNdyE)ad{dwTb@U$RD6gHqclHtm%o_X`g3vH z=!n{qy!E{N0OUgthfU8=`3OiOha~U~=X;ua4qrIk>EOAS{UjY^C*(7yfi3nd9Kf^? zFgIq28nOxuCv^uf>eHEtGKC$?$9^K>svcT&5KYn~dFW%R7tGF%&Vj?3g@hOvJrdR+ zK_?TwmwNi7r3tyA&0&R6V_VY|wlgZTFTVZ1d{x4)ocm;4W0uG?nKerxPvS7=Q~Vz0 zCFn%1DE|U$Wu+`MWbJhHpVuQw!zkx zGf7I&GqgLP=Rn8~>4-{|uv)ORoTblVkDXB`>rj)Tp?F$Hv%yLUb44PmUNo3TLH-x& z)g`TgGjoP>R0gH7g&X72Y>Te3jrq!~LJpn4S^a-of0lDeP1+@;e9jlxJR00&W2Bud zLtCqj_Ke%awds46iWn=bbdqByi!>0*9N%cDI^AcE=Y!H^f;ntX1XmcTj2v4AyJTYy z1C3pst|+P9ApZltr`dD9<9Ns67ae6M^rPn?urG?UR$#ToV5b|{3_P+liIz(iEyrRw z6UT_w23p*~F>QL#S=*Q{3|$N^bgpSG&0Wb|+2)4wg39CnG5#J(I7kopz;36_ zSs4m}#tWw{T*FJ_1#Fx)N&F0Iun_jA2mu%sGeDILtRq^>q-#!9O~c#egug-`7_SRVdkw0 zXAbfh4Gg1DxMGDmLcU+nkknxsF;>> zSi~5U{u02B>U*kNvp;gY#cC8YjVbxVSTyN``{`mVSkB1m#Bhygd@u6^OP>;8PWcaM zy{dGAzc3ZBHIgL}LuEKP7JvFNj9~mU#}`U*-bLb&B1aQaZA;_GCYUs1mIl@|x^@jK5eA|W9IeDEtiv~=$Gk|W!&p-aP|2DOqObX0oe+r`77xnLl7XbVQu$n_R%My1qGnNkwJ zk=K+9ojiqWpX^SmEJ$qz;Cs#oP7fUKIUaNr2l1o0BU zQYM7vz77NL6kay4&-~pgI6(=5Gx|jw6AU%bqaF&*HQfbvMR8^IHRYDF%9vIWdom2y zkGCYWJTO4y3{9YX(`10L%MNW~alaR5S#Ok2dhUGnN-(V*Wr;s0H&pkv51+2EfSswJ zJyUn0G@$Idb_L$&-#=Vk_RP)hrm-0wuoGcIi*CEedLNbC-=7|DFU#*q}k%HDX(x>RM!-h6@>_z zc}t{#z~>rLG`HH6w%fo~*cgyH{>j9afx{k2W6B7;QQw2;4&sD0Gt@VHuE*~eM8<*8 z76X64ev#d#=Em|frvDL2mAzDjIhvDPW8oxE857A(D*7ry;z>w~yf%RE=^Bm)P7fUJ zIe4AK53m=<=<5^bybfR#f=b31r*R^f>L3QJIa>SP7x<{ z;LT85+4;i!1@%kn8;Y7;%p8t|jQ2L4mlzn8*&Gdm^T?PcCOAC^4>Q83Q8XUsE)?)! z;xdMH;Rp}VrQmq8RefrKmXD(;hLUbiH5|Fo5~^%%mDgGQ`24GuBe_<-k7V{h7jUTK4qt#gYt-OM#Th{EYOo#*Q9es7R4TH ziE@_nYzA_opx?le^HG~0W#1XqW9#zR>a0Bxk|?c}T!XcVH<*LbYDd=|x)j9U%Qt8% zjI!^Rr*ues$r>&tBW?0@EvF;LJ?8_5MhEd({E{4~2gbRE%IpfZx$!@tVz5CMg?1J7yWpm zF4mYrlEu_qO0;Z{?EKhcdN{%;@+m zQo?zMk>wcFxL_mvSQf?ClHn}%ZItG6hGfd91Z)*cW|H8_^5iS$vY)${gxeqBBx{qX zvAl-v=8!jZ&(idgkZL#%e+=F@!|qC7xvUjsZ%OB7Lq9#yuQdpUpd)%FZ&r3~KOZEH z!XhN~9p@v*1IInbvyLNaij6))_u);2zya zhonH=tVJE1F{3lO9o;3xHN~Y>H`s=48PYG(TXbR=fi>)%sm;}t4(<=(a38@>EMLu@ zEFp}s@BogJo4hNzu_f^pLA$o46ZS|rP_MLg0JH&US{028q|oDvQH|8LG^q=K66iX5696Xk3`G4uZu#$iNLez$qz{IxzZzW(&`8*HN!sRg?iI znR+lc;1Y{zFO!{hT#m?7&sJWzB>d0tQ2y}%OarjyNv0kH$~^zbw>%F|HVEYD_i)6{ zS+6p99qH{PJXmsGYNmf$!=a$nQ z$0Nssg0S|Z*em*3lZ^ixVmSEPqVYd>v~?L6xold)&j1<0opO`<;gYC-l#WKLQGpb& zk&j^9lysN4SCr2vUtk;R#ObTBK8)6dhzVxWnCwHzKoXWNGX#YHV;1TFN#WpBml(T| zC9t^omT<{rp^~jPl}gF7v-QY72n1`$1nH*E1+!&!P}4iS;m+bG*^Q0z&9KIr0`ySZ z0QWAfBN<>}Jrrj+V_k9!TkeVhDdd5t^L-TtctRew^Z`s8%Jcv5Pxx7Zw8e884hn2b zbPX#r(q;*U$lCK6$s2kW2Ga$iDLfR0!97Tn&>MT{4%!$3AlO@aXPu+F#NAL{Q$53K zt11OwsCEEKZ(}Il07!#Wboq;)0^GvJ|6*Wc2-t*d-Ta=U_6MmD&B|v$1?~`^?#s z=`}@TSv9$_GiIgDNTd|9FwB{v{B|P!PJ> z2IxISz*v1^c9*p+ANuKA7gL#9VUpS&*HLyK24*>qB#w6~1PwJ=&f1=cdQMXQrXq?h zc89-*0~XTL`@w_78lx-WlJeT>8!T8z3NUmZz;a7tiI7N4Zz;A_K z_GWE?uPCpuE2?W;Nu4}>JHWD7K!YSd%)HG8mBx_BN8~97$CC&*i{)A{)+?_DNo{C? zu_u=OoL8QCg;+XgU@4%`@cD#+S+q6pl_0n?)N~lA>%_r0Dn8F3a1ElSC}1|)lL%t0rcrPS%@u45hY&>DNRGehT|s;BHuBOLl@PBoBk;fQPT;EF*%9=*Bs z6aI+v!V{gv6q{`whk~+-2uT}?5?A3IX2Fg^A$pJb$XA6~9!^g9z)44Te4LN~LoTNQ zIZqYV(pCx&ENr+b5+4%@AjwuXVU|X&V+lZcH+;svht@}4IU5ndy)=947{C^h{N_@( zy>Jc>R^v?__>ST;TpHzw8D~Rt;1dS$YY5wccetLu$HCFF+hZsE1&(xdz~U14VL67z z`VhFmg1yx`^SxX$r~4BU9rB`V2j{HBz#&ERNKY^5=~}Eu%G6H5I4W>7j;_R2l+UcZ z#%;JrN9sC@<~E>L+$a|0wp>bM+_mNh)+)n}HtOMrz)UkNHMvQ?xcz|eQlox&7@#=- z(2U8=lp`syPjo`o6b_c3hhpJdZ|N;*P+8$g{_%87Fw`97vjo>FtnY;AewKR$JLIr& z`qZbc%#K7Pe%R7hikmPT=IO&$4@$&>{*pl;8P==sp|!r1TGUyH4~V7nOP%oaCvg&u zIH~u5c~9B=2pg z2c{x-a#4nCqlA=X$BZ;)-D=`6!>5V1ZLY!rv&46W`R=$e)8ZarJt?dE?nY^;hEU^Tv)vivi+1vc9 zWj7w_+fyy(#T<0VCA(`c4rUjy`DVu~EeJdyw1)~o-@*^F=cXh4Hc``1yn}s$A%Tzg z^5Jkb1j3D#j-oIt=soJ_ddlvQd}Grfj~01qhefM@s3KXux(2SWj`(4>Hc?Ba(ME(n zzaL~$B^A7_Ex>XmzV>pZ;M%t<8({hCkr92RQ>*CkiT=+Jb24Uh0<7!cgq@)HHoyvu zM3eN=!L6l*2JK@F$Fp`{po5*ov$DZkwNp68*n2u3kH0tr%t7g_a8Ocgj0#=Q)dujh z_@bt#3_5o2(g#VkoA{{gD6nFPiroWtF^L~LPT;WN!td!u%n8;w%ft7BHvg0$FDYyW zMkL1smb^Dd+&@DJ34BSC+JMtna4Hkbln-at?Xc1ljrr`Wyp{0(jKt>!EbD(ZR};u6 z=nW6Phds8&boPFQIQeLaUmYEyH>P~q^zO{3rq(>-r-is2_UWDfS7f;3t?v%)qmxhGZDRnd*xO4z1|H-qz>&Fpwmfo~}ZBD#U$jNb5u z9M0MBJno4SnK;vT)TgNMTAQB)Hx7INv$PqV@WD1i?{I#hkn2Q4abf+|+yiZ^g4OBH zK3S(LO1)X@<>Ye>FGCLl($9t?Fmp0*KEy*${Cp0p%NAUu1H+@xr3lC2+Z<(wW$Lru z0QewV8im@Hvscv`C+v*d;G2k+T#xVIoKjH@!-~;nAO^m|`Y~OV`~;Ybtx?fz%moXI z3&{p?MF?e+W-a{dvT^5I$V`O^V<87?ZOvbrZH!&A=1o>5JF_`FPjeIQ<1V(qh^cHgHWmWl z<6LtXe*P=GZ5N9eOA5{&=j_t- zKsR`6e1)Zt<1~=v+k!_vnQ=Gc%_9vw+3tLpS%;pgXGO>XYx8GmCxy)JMUVQ7XH=qY zL1at#DZpi6;EaHTDr^xD)$UyApP0A#w@R2&|9I_X4nnhas0!+2xC60QJ@L z-~1c@_E1Cp?0L?nhy8f&*i@5A`i3=U@%~Xb?4dZz3P*)nk|)qt*Oka2&P%R-s8n=9 zk{;{sV`Wwc_T;rb>KqHM)K)|VVcUv*@Pf2K{`alU!I!{MxD@$4utyGaQ*zWt{$0IU zp{At@v*sSLL;p^fp%R>xeHRDlEgDj0ylm#2GH%JXOEKdX75j`oUd3WQBg*OFa<1*r zYkTr+YWa5XozJm?1?!f?ur#q!d9T_iEwbBI8XK!itTI*fZisFY$)(REka(5&V2g9~qVuxO%m<~^QKN0-^LELS>I_rg(WI50PExF`&mK?B6@a>N;%6q#F+PQZbB}9VirV+* zQ?H#3os1uNWQ9?fg1b&H!LT!?6n->FX0Pnx+0Fjpi)ACwRD;1(w~$7mG-F0Ox}4WA z6c~Ckqr0iLXz>a#no|as5hB1sSkd01DaX-mjN1GrN?PF@wj~x>)1?{xa+KnT8Xl~$ z9_>AwE@lPGcixQLDRJ$#c*yYFq7BCW{XZ$k(FjS=N!len#0=^5=qP#Y`3mNF?C0Vd zIju>_EUrTwRbf`|)SawdY6Y;;C>%(g=+%dVck9$}()PdkcmLj>{9FIdpZwjw_n-d9 z|L*_%5C8jR-+njZwo-T%H9q>;6QyG)h2Hca9qGNQ!nn+0=Kj8y&TtZ6{8?-^#&fd; z!~OeQE!wN>Q_rHY-CN(|ki5*}ojC_C6TdAALh=+TA3=@@hmvGZWH=lbfEhNt%` zf=8=AQQvyN;{=J9FQ2Y_DCr zmAbV7VaJIBrCHWSgg8RZR&~BOYO)~HqVmCRNE#5Ro2cdkSZG>RMW`k7LG5xTK-}COi_wF1%%%3|mckX@n?QWB(;>cRR zH*e<5xijaUbLPyMGcfzFDm7e+4UYY8hhFJ2nGJ5%r9aa7SDeiuVWK**gzPAH15>RI z(N;Q|8G?xGgk*iYvxGJ~{lc1Y(b-mHSgt~5$Oep%8I^`|m)kO$W!5wb5o699@a%(o z!J)R^q}L8eAa#fYf%JvWEIIFo!ld2HdOI2$-#+ge?>;-c^WK|O|p(QElFa-x6@?Cj_(T+@!fCO3+ ziON$iTsx)oX-aGcoe$hihhhvpm6=&NYMGf*lO9a3ztpup4B%6HhX}ZOY0}+2yLI=T zzD-+ZXQrEt1%)-RX9F{9=kCG2OoG>)-5`*F6p$%XN|YMa?zeSEz;wazz9Ks$q!vSD?17Y}wAQ~n zMDAjRy)*ChH9zy}vnQWB>iQr)bMVN=x9w2Wf9vea z8#ixx?1_`N?jBV2uU?uwF?#4`V}VknqJvt3R?6Id;Lu~A{q^6sXcc__;>2H%AN|p_ zIrUX7Mej5C8Tj_ump5+SGCKBJ|F)f1FHN35`*Q!b9WI7XA3Xf2JwsP6PyXAb3$AIx z{?xh)Wov?@W*A`%HEC8m9fQi?k0)OMt zJ0u{@Rx13ROjwi7XhXfEmH;D>%5=+gvHI1wIK3SO*KSkcwGc!P?5hP2H1JD9C!RlN z$$jwdIyzuxO6bfR*~Y`f%g+uUe$1VuW@F*R=%Hg{uk|@b41m#*16Q(@7mbad8`wLf zzMMaMYTy3hp#J%@r=A&m1YjgY>(+B?=}TXJ`oTjZh(WWl@bKquH*ygG#>UTE{8JYv z2KEjq{E7zqlMAO$9)IPf<5Bz$R9r3gxIV5}RjtQwf}0>PZ4|WGB=;^!)000RjTg>G0vluKeqyJEMJ@wvLUTZ#EWYeT{SHo@_3I zLjwm69%(k-z2YzU_w66nQwLA~+EszN$X5%#<~2+R&LObk8p52IPuWS8n&>nd#Fn9iO^50btkeLB;*qQ#^NccINUs^KS?8V#jR*cl^R#fA`{_ zL~-UsSJ(0HK0&d4Mt?a(+I}aPSUC=-CE86PlaN`H33VV0)X`^qdm}yHi3tw%JIgmA zlf=w{bN_S-9W-w&xbo(E7tSbIGIjAx6>`3JVZzj(aLY3>lk%;%{h}?O`tF2!YPWv> zww-;OwnXta8w;Z&2W;ipndv|}&dyxEa%uAVwYmHNe*W<29iP0bSkeUbyUV0rz_q`f zvczPjkY?H5lMXEECoyRB-gUvoO)qF21lFZf4VsMwk;|)OHI%C&U2v0-0cbYfEvZic zQr@*|&?S;$)3y-n)kwp3PexyOP)WV6ykKeGe1{ep&Wkt zXMgU<@0?h1?`sc+37BoF&EkM?{%4)-V7smbNM8htXkI@ zmvBJz-HQ=GI%bQNm`F{^OtN(f9+uq6WOd?R8-E?kSStX9F`B`&tU*sudFa`^V}bD=;|8NCxyii9a*-Y1l%m+)nwKXv=D4AJCTBsA z91R(WdJ0wweh2D~(Xy!YDv*Cj4gdM?Z+!D_pQ_wsHDnahkU^sRAa$yuC|)?AgmT5w zu%_&oSYm9X%*e54$ehDuHo1_p!PxlmNszi|Ef;`TWLrnw!qFgY0n{j|7cf!b zpskwegcCMf)Nhwg;>2~@t-t)UFG?9*r+XyVdi~80|RYJ zALu69_R)PPS(8R*t!OF*D6g*)LQS!C0cX2t$&2n>T5OE}#iNqbj1|})Q1|Xhl zRv`W7az~~&RhV~o#Td&Q1>KO*xry3F;3nEKte@Cafo zr-I=_`5}OMf=;R>Xwh=b6(J7sFFpT<^FMskdz`>Oz`4(y!^1gFf^*>j%-5icKpq4b z%n^iSnX!&Q&JlPv8+0}ivSg`mV+NTcV#181GSRd&vI0pM`@|JU+CFz*GJo`(50!VZ zIp&>@TKf4H4nUic-b8J^zE;V=zI%s}5K`>>)d%B97;nw|z!vYieNa92ZRKmg00006 zQb|NXR6n2XIyV~&*XHN+*_~kJDH3#2?wDC*fV~axJ7aNw3NF8K?)88CtE{0VEdL?} zVu?Na!^-yYvYCiU;DFbI!bEcgw;2t_B4d%!WHcBzB;Sy-$>}1e%Pc$kpnyn7)Byqp z{rS~7dcu05%|ssseGK$5&{kkGu+iktwg-tgPmi_h3xvOUsLB#6y5CI2r+0%+)g}}z zq2@K%b^GA4KmNw-%=CQ+54+`FD9*)+M@F7jpZDz_e&Eo^+<#3!^5v&(g{cb@nnIC_ z17Znf^Eu2hJ2P$LNPp|MpB&gbG&^(oh4G_KE8J|nJO0$8>a!ckz!GILAE;j7n5-SbEKi?Ow9-m1;lHl*p{94otIgmTw6 zudPA{P?fQw;!2YgJK`AAk5aN|19flaXWyLOw|{uw{$aN;i?t#ExIRBOb#cPhd35Ay z_;BW%;|!&#WoWJ`v(%-E@coNt+yG_$*|%^0<)2S>TCI#lqP!A3sW(sXmNj{Wo0U5Y zpdubszquV5(}zIn&B>0{lTmGTbGM_rYRi*E$nNIhd}>f*#RV-LH!so7XKee$?%rBFR{;^80?&{jV2F~J{E7C@Go9KLa8r#2wb{Tj$B_Aywnj-CWswQT}z zf!1zYfsa66=$Dw9()T!cykZaMlkHlvzKmv>v6y06q9gqb1(cIm4oA{w@9rkFSQ&;4 z0QH=FN`WQk#C_>I%>W4+8$UN-qTpXSdHnRr^vQ1p>$$S97uHA!o8!348(qx3y@(+-g z#ML3%w!APe4%J_E_@-rCqvWjGT*>DmL;+6ZCY1GDtE7LDJ#G{xC+)iEYvxd1%vEgu zS@(}9(0Zh=God6lOzhPSJxLNET%Vtt`1@ze2n7IKpPxHFKNq4;nUWN>8p)0x`h*~A zNL$X$eE~HD@a)X=Z0?w03@lgqTSjF}7W*w)xz|E=IBbJo&c!rE#VQ%q>6S}K)@h>x z!D>yiHbVeyMxAnU#?iArV{!1$9mfK-Y3z4SK~;iy1A5CNb}EX84zNxlF$A07-6Hk1 zLa`IP5@k46EY>QZv4xSLtK6^kS~+}~N^Yaexo}sXFEb=#oq&Kvm2tLVEY4EJdp0IH ztX;%d>R%f=<7zow(sx-!gRX!u-=cSHtSSeq zGjrY;cqZ)$@%uC~UC4t<&bfw2ix}twf7;%`=PF3PDVd1KT@$dQM)FcBu;=YhK#L)S zzDox{t&1lsU?Vq|RHixs0(XE^CvExwfQ>{ZAc+OftO|8?`0=T;RYS1+Vl-1I|5avB z+rOOqQ0~SffT0rXCPJ+Wg9es$%zc*G+wUwU19^xVQOdFd3N&pDUKsz1J?Q+-wYR%B zCYMEo&&85p- zHU>Yr6gh>87uu{gla^vzTEPoXIsa3B)c~_;umbx%LVl0!>b@84!(i~OJ*t?)F`TEI^r8?7BsWKN|D<0Iuu z)=tb!(Qys~GP&dZ$lm{KQZ>}VW+lXx{lpMNU@0L!DEs~Yp%ARNGV4LYX7{vMA=g$BK7aR%)_Xw*BK7xbu=757cvVemV%7lY^ z=T`0oS%XD#pvTXJrCA^DCBH1xeD%iA=2sT*GYt%&I-;W7L?Z>|EUesIS2i-%GJ*GI zub1iUax_Rc!eQzQ-e?1##j_`S?xqAE$fO`Vmml&b4m{6a9v&W?_;#?2MnC5(evox) zU%ByT13|q-^QTLPmYgDZE`2+Vb#=nA=m#eMD{k=Dpcc zW#4-ti6^QKM1CSVRb>9^*%0**)skJ@hLEoVE8$2|sgw9No}`0bIxiV{eo6E1q=rGW zdnt!XqXrHhU%s&2R35)qE;5#kFn>>~hzb+Tb|v%ddR-|`o?53RxQs#bkVpU$rLB^2 zus-CRK#ZL8zPfqIsZn2|sbUqPfc%}5^pe!70gpbGkr_uk%Kx9@og zZ~ICu8rZMZu(l*blLC=}==18pkETi40(S@L2T9}+?SzJcZ6pkO47a~7^sWM={nW)R zGQR}V^jVIjcjF$7wV^bfvR8NOeL-*Qyqo3{`T6<6bY9Q{*y!ymf^^0@{N#%`Q)ocR z5tnu)bYS|tyo;H-Y@qUjumjJ3o%#ZX#dJjM<?0^VMGWBFxC!jQ2y$%Ary0qzH#<*;qk=r46fzI5} zROA&$z2VY6!~SEJdX})jp`YD1PT-JdP{`){%Bfhu{pPL4u(Xy?S&2YtL(yXNk(Ld& zB$so$k?M$ue)7^OCi-v^?Q57)g5gD7CdGJlb2B%kcQmRI-UwMBI8t$kH8tzw!4^I7 zy9sitjMTfp?lnX0#u$r6eNg)N<+kCe?@aU6L(V{SJU&NdQ%b>6$uc3ahUA_cd|7x9 zeWBpq!$T)yx*|Fa|7HX>;+A||oc+Eu!uUKqh0m`!6GMmQrJ4oDh3jLVZX4f}yTDe% zsO%PzgC!?oUJC0B#r@-(W&ZtIPt`ecy`NPgGok$uB5+8V$&uiX_$Ps7(2SzwV{{FR zD}-tFVdK=N8btvOo~;<;y{Y9z|JS7{KKX>zA&s;5__Q~YRM&2asFw_^Dl|jvNqB>{ zFm>X4u6>+jqm)hJO42U8L`TFli0auzdHA=5XqU=mJ4QCd-@LHq!gzWT=SSVLa(a^7 zRi{^->X5nR460+PM#Nvs-J~~ITV4J3K>Kx!A0Cc*`}vvtBBw3@X2CwO^IwTv*rirwCIb##1sdlem#8#4raQ2TCBLYV0W)^daPi}2Uip2I{t z^|7UXoB3JlHf!cRIN0>{u|msj1Cv5e|ctqZf$avsL2l z--deOiAd;E_az<$GjW5Gp93pG4O#_r^7CUn7c=cqF=uoO>38NatSE`%WkMZMqQ3FF ztBv)YZ}gef>Z->eDuL3|`sXK<6Tu9P737Eba;afyCJ6N!5_D7(JDT~`(Qur9KL_G` zB*ZphB4IwYo!vFqt|5*=P7pHkm6J>H>85y33XOVov%a#h*U=gNa@3m+ez0u(x32e+ zGaf$V&%j6&Q{VA)0WojyifH&joN~TPpB+iTKw+*tP=93P@- zx@-tE;~=M4(kEm5t!h8wmr;#iwda4r080JJhRVzg}1NJyrAKEv1vL{DAo5>1)bVE?DX*}$G7l{Y*T0jHf_x-Jc8Af%~Ck=^~yea`3 z;9jY>eVUzM)1C8Q>^B4+r`&7GF0S{kE-uznrD);HECoXq^~UeX5i>NtH724Wt}E^r zc`h0BE;*sZ6A5v1T1OiqHvC=_(_Jj=_Yq3gOuucP;|m9_9Q&;P(BaFUh7~qNsR_xT z4$6Nj;FN{_N+f$bzH*XT?`EW=K94B{<%vXky!Td(&txa34HQZ&m*`#`obj+Z zIucqwcOX&PH_-?u50DP|D{t;SR!7i?AACkAPyQY`_w){Z)zhdVx{!gL6|xwcF?~Q@ zEW15%{Z$mrD%mTkpsI?+V4XlkFn_u@&khYh^xZ42`CT8PN~}NHJ9-Avzo}Gy*mWCs zHFMtBANc+aQPM5;fy~2FBs@6jqQ(&r$)5+L$5{gO_$EIQ*vbAu+kU%Vuh*?+%Cf+< zP!G>|p?r^s7B@127;}rLL);`QM$|_lLsPcfrmeZ^4;QCxG%Vq?cfl75glh8r9%&6_ zjQUct8Lc(Qg}vlAPYv7~tajM}@`=h7`wt_=W~aA?{ie2xATv-TU;l)nC2n{V=3q<~0{1@lnqKwjq-pLk>S%s3 zO|(_Pv8$SP(TTdo#`&-nFuevD(8ah}C65n_p8T0*z$Zy4H?wMN6^wat&n}r%^*&lP z?hx;*J^2O_Tm&>C)(O9zUd#KeKBe_2TQ-JSK?LbZ93Z!!u+o=e+VDuy!_xghi(eLF zfcXcLjg^Io1dGgaceCiJYSb+rG1K^KnuP|D{dtyS5pB3^uxE@nbeR@`+Uuu&09=W` zJ46a1=T~*7S8QtyfP2S_MUyFt?6~$_Tm4^joxjq+8J^VLy)9d*M~jpVR<|Tz=rhg2 zF)tP0jAHZR*q5|{Ce1%*#N_1qt2g4R_5-K_c>9+TZ7aS(KlDco35`hUt0f?E4&&J? zktYp*5FQRDL1*Oy&`542A{Q5zv3EvF$;)Y5^I9>}pCGt~2~-1k27mS9fr|;Th#R2Z zhCG&1p;C~T1uZ@D>G{B@9Fb(;20LXAwwh0u)8M)j-|cf6Q@HH_TEnnZOg>$&zZj@Z zJzY^Wt_deti5G-37y13dxk^l3ch`w8`7nXyY;)P0fe!~%g-3K@yN^sd+ikD5eyc4} z$%~r*udjPVe6R$RX3>b;ASZGj3rxSY7v@f}n~akB8>*cOePQSO;7t2OG zMm7|dcDNK<`!@BfOD?smJk@QCob-H@AE3BG?C{&K_PW&W=D^`1UPXcHvJt!_D23Vw z(riOAej7B*w)x{$5NcJG;;sMOq^_a@$UOS&FnAHKcKL_e2*Hm4S#=2AA=liYjeX9N z(=EP!%>sGlX$7rg>BOALVG2zZH;IkI*lB;gxz(gCw?P);7(Ve=Y7hYdB|bKR9ltxbHN#6(gIB1w zLG3x7%}dIBOxynAFVf(3z3$56r33?6&@NjYo6F{ zSWy_NzD%8driCE*5zNLhC}E4AzYGr}mmqA0Q|SW;?8E7FqtNjh|2424;2AQv8R#;X z=eqh-UjHTZqDT27uW>qZXuGGVdco5%`H8x;g~m5Slmzjx_YP*@0zMjOO{6g&kiJ`p zca{2Au*yQ?0os6L5%K@ZyWDC#Qj?Hb#SmhN(748_pW6gt(#wHVmDhoU%Lu|GtC{51 zXHEK`wv}3^H80zIv_F`5dnLeKxZ`FmR4c`5G(j^{Q;*Ux>B=UO@P;qrP+Bc$K?|!F9rna+%aMOwjGekr9yam>C=>0-P7b6 zB8Td?vYaP3$r*)q3Lh~=ke6%UOG%J8v>L`C)JPmloMuYY=pQnh@H-ud&hrL^>QX6WeIoX;U{Eb9ayf{c8mm2Vu(hNi# zGi(Wp8sD>!~43a)0>*i z+ezIX)WW<^9#Teq8D`g-6AEgiy2H}s(c*aJ(9hDC`C_!2eYLS?*6NEfDkMlm%9HnX zi3Of9Ik}%)F!G;;EvfnbuD=oT+}X(qR(6Up9li|NqwO*65KE|0t!&1mt&YxCMLT}T zu+o7hRG0!BqIoOq8&FuVw>2c8QIIozFuv!}YpN6j3#xwVaz5DFLJoRd;{UoosaH&D z;8?a8o0=*v!uhM8iXhfJNE6aPG=HYXk<`ZALXr*`Nft*J&kHkeQXK!0IuEAR&j}H) z*r|)E3psrlpFv`luTWCUKw#U_ri#FWEzZ(XW)1(v6H*^~JBp#XSiYID@i#@A_ez#~ zTVKm!b#Hvo5!ZA>;_falI9ANI&|3jnJ4Dt?Sm~xe`M!0_ZJL;w1znCZk~V+95N2kj zKMfv-m99=i$?#Df)<)I#TFxStlvIe1jF1rW9ulH(9A4l zF-GJ|>;l6ww+fMX>%E_S-Q6uPqnF+4G~yrTUo~PTZL6Ue%~I~ojUg3P1lZDXE}<6JC!p=J$NQQ|&UIA7(}2xN4}a zf+QZ05+Khc@%=72A;ql0cLwng@W*k>^;dqBRzr>M&<{geZmh=MwDGBvWe1K6VKyVCLP` zY=vKJ8z;i3Uy=1^w%*_Rejo-M6f*=@d^J`IIoLs9<-sA-A;_~#AtC-*{`{DU<=1tj z92RXNIID1BgXW=B#94@rUXA3^H-LHSTk6blnVJ-LG|%CHx2&PO&_*{Nyq-Q4Cqwgv zt31m`CON9(a{U7_X3k4}Y%RQj_tXWYA)M3&%9OKX^CVL6{Ny3eKZbt+>X`0xGew&RZIjp zXmW8TK6_UI28UvST-lRysKoWqc2^_l7#qN7RU>p^9xdk3div;7e>4Ubb7Fazh{Yx0 zBOz8}0Rr>s)*WJ*OOX1-H%s*Gc)CWykyL-VFC+UUtelZmwOY8aqsmZCiq`i?n+RuW zd;LXaj;JEd$jp`*HJ~mR`f%l=B}#OC4U|`?A;# z4slo&SuTeV(KXa{ty9!vV7I7A!hcJ)$&*Nztu=~3rW?IYlm)&n+h(4cNd#b-cb>qj zW|ZIWSH;eTq%-m6h!>w=VZHpWDfFD&dq=OcSz{d+YDm|=Hccg`V!OB|)gwEWxVWQc z`}B0y>7&a)!&-9+Ul~XF>y4ye>P9^%Fxz23i<%PP_heF2J?9M&87ob&QFO&^-4=m2 zqi?_GN2M|u#CBw3d5Ze;C6=t)@~iM1epBa|e5$?_v60kXdm>d_k!>2qSkcevsVQxF z49WUYuA0C17E0n(uN@s&pnrv9kX9PRVL?VM>{ML3DA`Ba($HF>houfJqAs#i3v{ZY>5)|$ea`kMA09nFj#aC z%J+kOcUc=Xt6bXjVYUrZ)YXs0tMlwAbm_!OPW2sM%xwrQElg6Fzvf!g11eZ4)GSBO zQ(oBno^o+`0fn8#i`#~q4r)(hr--a9A}227;UzsDAR4t3; z5Qn-*vs40Y)Sho)qL+XyYVbtbS=*3DOS$4q?c%vcwaCeNz6P@naEM=Nj`W+l8ZJDbdmfzzr|FMij( zN;sluM*(zeS*=HMQMfQ@prqTn)efdJ%{~3I#n+Y#U&a)noj{2#H#V~2#FyDC?NDKk zxlFQ3sY5iD5Zj7LP~xb=NnSgH0=yWc%SPnqA8ENJ9|y8u%qPOY9axm#peHXv5%l~s zh@5w{SgFW423z(gVph zah5mj?=Y>W67{#vZ*Gv^NtpAJ?bl3Gq(ddMa4Oo$Zh* zI5~FPj1M0#Oyf3VZa8eQ1J3-`l5bI72*PNBVHy z=z$PqVY5%DaS_wd1iEox>GBZ4ID$g%{JA|x&N6#~NK`Q|z04Jh4O7+bwf}LXOky3j z+HrI%2EGK^j>P!>^@}|QwI8Slk(&vwD=QC%z z(PIRZlNg$39rJ+hL@6``x(dEFNQy(Z5dyQ?AGFH;_+rWs_V+(_)R}?ITS&SY9}5^y zsGL0mLWYJ zz_+IjwW?D}Ee8wT#f|B&kPkp*4xNMZ@{hPxW%-1}V`c;o=UBAt$^=DP&1`I?Q zC`;#N@WLO9C4-A&GsF9FoNfyzYS8uXaxMv^arUWG^r!;dTJHi8=3f{MEJ?rEh@RJ_ zXA{X|Iiqv|!cR7VXdW(VI;S6EviQ(h|Ae)% zG?{Wkm=|S)mw6b$IPa}cHHH}Qr{_oRPmnyNa{n|@>F;_23ZHRTXtKTzt^X{PCQig+ zumf>(wlSECq9sj&Y9oP7aw{(F`T%m?BWNEO^rqVHkEgxh?eKzCP!J2WQ-!Vnl@B!T zITtoM7(uu)+#r4`zY(9(X7kGWcQ#!_;tEm?s$^z=y!xVed*V#l)MWF#P{XevM)S2G zfTK`z;~?qBA%~gw`kCG0xt<;Zf01e;r7{VcIapJImK-;u1VrCVK*U!M#sTecq)koA?zl|lvO$AH}$Y=#oBMKMYa z-=FtQ|DMbsn<4f8x{%zY6rV0V(;=Vk-tXUG(lju^#AmnoYi%9{Nv4}U?t;|?S3uv+ zZhLieeNH6ERgW7{=+@;aE*g@yBm-y*RNeY@8PSwTe-yIC1$mW_}; z*s*;HB2anU^?Fku^aWOE7wWi15o=t=A8!S9pGBKrV0*^fjOi99h!`!T4=nv`MbW znhNfgpGWhi3BvTm8VKxlbPLA4YvG8065;aU@80<#Gw1)ogrbDNj+{sew7XhWDjJO6 z!26jiPA-Wuex-bJMvBg{w_d1lDVx2^mI;lePR^1^Xh03Oqa(^Z8eArmk}vOKh9hyQ zRR0C&U0B=ttfDrnGAwgBX&@U8?wy_4VmLZVKUElHm;9{3MnIn@x+KTY`O`rYHgFzJ z1(`p-DV9$-@od|m8E#*3u41nMy{>ca4KjG8L`0nU>V*jVEr9Tt|Tt&__5)j=31Y1eFv!1u@41F6RYX+((l zgJYFqos`8^m;iNmhq)@8bG&kk5uENKW1<{6%|MNc&}diU36>3>rDsUznoBzZU7{Wt zJRBTF{eN5ld@urCO(?;wDu;hEP2&c)ULl(7DWmGhzrm}WN-27Xxl6MIKx;G`4Lzom zv+N86F^4odScUAKfs#P5OdkwsSw5)QHV~7o@BB&KUN84Cw}OnBh{iq2Ap7eKb;UN# zH>$5>i3-^f=Ax_U(D^89r*?LF8zoF>_KYPSKs6>+B_Dr;A+JW*;^B{*1e=Gble( z=NoNXt3~oUZb(Y`Wq&;0zm&swoz<%pmOYU&VM9~P5I8Cf>|=I zwWoLSV{2+8kUsC;J`9OwMc3#vUQ=5eAuQ4^Smtm<#7&0Xy~xf`tp{1%b}~eNW=V)A zn|gOA>rVVWR6Mx$H2e3pyLL7J{4Yhu&e;j5ig4(5IY}YxXQkI-WIykS#u3cDQA3_W zg0F31vH(5E4`OE~Q4rwtjeTqg-_80=P}JP^`A|-XA_*md(#e`4Z%VNB56Wdt70U&S zl3Em3C6WT)hNQmQvO!QlZMT6d)!pES!8QMB9RMG*yv^1RWU%pSg$a#sg07lo8tNoqf7Z z9G+vOQYL5Xeqv~r`pafFNncvXfzOyy!65nD&w;G^Xcn!8y+LoL)GL2BbQx6elb&S$ z_`>#;v64|?R2e!LVtV6W8$xvB*i7~@8nVhg2AV*q6fpN1WCCK$2)3Kn-+6>NG#@WB zKz%+ihp9JlV5%9um`(}0`|9W~z+4S25H~ttU=Y+!h35oSr3rfoFnxJk=*xrECvWn* zs*L1a0i43{s$c*(dP$@*yYjq^gIduAjd#LarKL;ZdO$S|PbU zP1xP}M*1p;DEx7u)<~dTBN7QLBpT}M`JEMfQ=m}8#M!Kg{txk^TZ%;lY-jaiF}V2o zMqF2w70f&HGu&WCk65SfbvzeJYKTpke{t3=d z6ze|a>ODkl@9bed`y|06D@h7BaPV$&b-@i@if`X)o1)JB%fsBqp>!%VA}v@M;;Te> z`X}>m2p&87bhg3#FrLXY|0D(#Q~3Un4}5fv9&GZ_w#?JJ}j+KejlN4EmWoSSXZy6k+T+xdtOj5z~CG@Ox3=cyom2l8}eh&`4kz2oK(>U%TibH zt#a|m&2TgfsZ4Xr2s+Cd8^a_|=QLB_nru$C*iX#jn{)0Mqv$WJGJN-Fsu&eT&*us5K@)_Hsnu%GxW@IE$! zeqUt~kul_qDaASfDJxs#yj{w2U8;2;4){qZ-eDY4Ve)KKS($^mNW%8UHBHbEFZtW? zFVGx31el@`W6)D~!vjOIEJrGjkyBK?Mkb`({F@p_Xn7rt*wYUV4lF4x=6zU^B;b@m zyqS&4zwuxoyIqZ)H^Mv|VH~8v@ktj60G3I_gVWF zJVWoRg`uP49T;f3aP#(h{^oILuMqI0?o?CZ;1~xXhgbOCX-NV8>QG&U;Zia;5roGF z%R3^12eZ&7USU|p)b#Y8i&@)0Czm?{{ec=st${ZU`2ciRSfHv+7O8?_7py}D0lv$% zq~hKV9xH5EbAbU?bAb2vvVI#cqVQ=)z=>t*i3C?iOd5Xz9p{q_k_2g8=MF!E|9mdF z9?DylkOR&)TzD>DU%PNk>@#5evK$>yJ{bQ|f>eQ>n1&P@kqqWAltu!4rWzDmVkxO2 zo^Y<#l>E%FxdxXO2PuF~O-(`WXD5-uL!`F}ED7}8 z?<&IVf;6azNey4W79W7FN+I9MoFBI$4`#(ab{H-P;|BuzS)w4TMd4xkMl4c;7H^!3cu?C!AOgDE2W{tE^C8zS&C)l|&&Gb1 zf{_V;7CjZ*(txmCU2U>veaunbDwTPt1a;IGEf+`MFZLU9r(TJZZdKtU zt_bV#E&HN`GT;+Ec~3e6C<8t~IS7;#v8Up(^B_MK#{)vkA8ML?auj7GMOnkmOo9y^kn7Sqk5;KI!IdUNBc|jb zKrDZ{*1<{<{o(T3*eo8>4^DzdDb+JjCc^pCt%dNgzJJXy7?Hss6HFXbZaRM-L-T zehCc3c@xfivUQiaDGz!quVg(Gj=NvCMg|Zsb9I|EJGXMr351eGNwe#nO%+3w(Y;X%fYeU*z|1a^Op)HYNQJ0lx-zjrCl!utW-m?0E?OcjrRe`*%$?o($#!G%$fMQ>+%-V>RLj7zz)Rq%|5@Dh_~ z{{r(;NWR_rzIbJJH7n=8xyao8ws?jJaIpG%fY`>09a<_3y3pVeT?M~P0Yzb-W8s4nW~LJnUv1`{mUXR&7aM1m@&u?9?^v}xsL@IK zpBY<%7fe9bjb7v=E(XOv*T34rMh7Q~NpCseYJ98m`r znD`-0WYOp)_$b#8Jfwg#C-dSqi+&azT+&s?pZy;R*|u3@Ko_kx0Wi*gyi6SjyibV9 zl_LV@0&t<*qKeF6QX@~^-{5~oMIv6g1CxikVW%!+<%fSL3%!p&#Gj=>2@_}~Sp1O- zw-BCO!{%h^LPvZ*3T^$m!M%$2Dyf)10;5rchV>*p=EB zP5pW5(85;G-u)Jm)lO6#kRBq$**}X+^sR==vmMJ*dUUrZv|LNK*x|tb5pfU62pG8= zij;YX2>}9RKqcAPWdF<~&8kCZiBY`|;A$=%ux$3Q=#%5Ab#X_5sU7M|-d_nZaobvb zyD7BEFoD$4qcc+Zf_fn|^n~!b<apK>`uV2AcE&@?QKIA=XSqgbx4amHwP@KK*oq-V6mUG`$NIy` zR#r+Vymt16jWR7vUUBiSgZ5tX4H=m zN+z#$fF`r4HTCs6w6CO)4hT>+l5=(2x#f=ke7CTu(J{z~gFl zBw8Qx-%$as&UJ77AvPJ29+aNChImU z?DFxg&M~zDJ)AfQazYAKq?2~I6ue|F#8YDWL6+6{#-0mrqlpXe16FkTjrUc5=~C}T ze$HoyUv<9z6c8PC)1;(Co&BSsoG21+?xyOr|2?se)ei2E3d+YY?Cwk&F_zLHMhI|u z?Ci69nXrC0vy*`|%b3C(GBn!>>R;#5Fe*g4kc;7XYB9P?@e#L=3irqWpe?M*go@1CN|mprjsJ|^Fp>pMzJM-cQC$S+$bhM3%svOBw^hz!# z2YV7;Pi)AyyQ}m|b7_{)VU#=dly_HSk+85@%S*5{x3yE|UBr+qI1?rI=y5)}}y8KvR7Y9fW#i6m#kJ`$K zgHA6V4J6ZX3`h;`idXr)ygqlfeoMNTjN(QkAQ1$BkMbnvz|3X#E*SI1l79O9?;Km_ zTd1&;xOqqx*$oqT1z2l_3zr)jIm5kI>}t7~Z9cRmX*$Dk;<&2Nx8Y(A>s<3A_U1eQ zSg11hI!8$$jHzqo%b}OfKe@LWf(%`b+v$g4{7=2ZW6P?zL@#pD;fYvnrUmzYEErRC zhA{G7v8^*6SpF;nfHC>J!M~NrdGfN(L&+VADK%9qjn@VI`J4RFJkcvBQ=G|FyD@Q> z;9ZkZlaL<$nZ=VG27(gI?~I@hbApL^$ey z1*_V8RL4KZ(o;tZd2L<=`&u#Rr7NQbx5C`Eg3GoN`je!)gujTO5mbsS8Mj`&&5L+)7PYZoMfMDWj{yHB3_?4p;7oa-_$F5}3?9N($T}GJRtu z31CoKT3cq3LHCPkif*t@MtiG{3u!2`)|P#-9Siv|vEBCMoaiGTl$v>8>#_{0+K(&O zzZJ$ARB&j^Nf+QCu3I*&752mxQu4RTS#LGVO3Tm8=fy{4TZ8jcUhW?wI$W+)&*K^l zL|HHc=Al4Xp3;)Es4)sNy%E=vrg*;vb-w*mR3J^SS$3h&Ohs5(eEb$=Z-II$0J}2h z!IRbOs4KqS@1QUkS5+oaRQdgn!1Pt0?p)BQ+9*pnO?v`HX_kfUm+KzHwi&PIbC>q! zY-Qt2o;J_+XU0rFV~eP|$zt6kzs@2-2vj6|D?s&^dZ1Ig5xFgpLvubJsFlTnO!UxW zX(mzjiMMcP<9W!2jFZKd+a!akEZk(6NOcnRD)Zgo%umafTz!?qrbvG)*5QadN_f$3 zXyWH`Aip1FUp>dP*5chPp^r{97lj*`t}TuClNcf`b5?UfTD#aCq>|6*Syw(HIAx`j z><^NBA&S*Hsf6Jz;TeSzrob zy8oi|_I|jQW4iBH2-R23WP#LBu7poWv{anyYXcDlM~S~`+&Yb4cNxXEf}FE{h_=-l zFE9N?WGomPMhT7f+3e)TuhPy#ygXbS!FWp3iXh_Pq2^EfgeqCh2XDi(My=JXENQiC zn?DYsB=8K5_@K+)(zGVfpX78$UXnS3XJfDL0=iJ->88;qC19?rxwST|)jhuHLUpx@ zy?nl!L-QRuV%fzO{DxVHyK$KFBL2D~j+3!N!jnJEO~ya>+~P)jIsDG-c{Kld^qpD2 zh3ng`Yv0SMYowKr*H?V5Ujc$VWiP>gifxJ5)#0lhUo;9Q6iOGaqQ!MErgcv)*7E8& zZ38v`eLZ~i4Z8EKhO!KsrcB$XDmf%pr99$8&Dv&8ehjj2M%MoLkR|cOg-j-OB}g=_ zX+sIr{7k^G=K}c{Bdc>15Zq&{Q8C6n+iF^CO{5zgS*ND>Ny|pkva=>G|50REQ{bQ5 zO5#M#Z)J21Y#l1U@dGOg{Z;A*@<^@-Z3n(&Mc3v%;`p-m!%Jl&oeePboh#F&S11+EXv^T)3A>P@{ZVH8Z($eix% zim}glN|Zihe5ppJi*FtEc$MNW*Ryuhd0V4FFAG6i(CNTiZjqqZ=LfZ*$JzStkBU6w z{=Kj1K@aKG)zv=}sDhqO+w%h-D}hhY`oJ5iw+pI?$7Z`}N>P8HYta2xH8;unVZ3*%Gne#ygbp*5qeT)fe)o~_?Pto->NrApXglmT9^GQox-(2 z^p8Yuzdmm&IwU?$=3L*I2E5!X^gT?vcE}?e9p60O953Wt!UnBW{{DOkL(5;XH=ZM? z#J)VHWCmXU#V_mQp-0 zz}uU?rzzI&tp?!Ath||>!ek8D)n zr93|{3!ns>u-V&pZCkz(faC6ey);jFsYL}R6R!_aqKTuxMF6%?YDL=b_V-}AjY zD|aOi+?i3#w9RBmwB4B)tf^|!BW;53Vvp?&v zL^ON0Fz+c6*JaFv<@<Oxjh`y_dMR0vZ0nQ z_HtBRUsqT6{b8R>^>SeXa_;QTmqNly_ z^#4%YsjmOtr(yYAX!gAQPNNaVsOft;>xP-V>g3}OT=9zG-@aLLe;jEs9g9@Us@@`l z?jr~M#%4F3CpRR(|MwYJ2aB38gflz+gIQVHKFk6Ag{k+V?bQ*KbtXgOirna@<~2;0 zz>?Oy_-KmrXKQZA@1AnPLa$F(+STnIteYqe6W9EC4GR*t~F`!k{ASfU%1hHb+m16WKs|YAdzH?)@bFnjZzL{_4o6&z5liYjW^S;mXyw7{? z-Qlsql`>6d8iF8{rEV^(5CorqAh>8n68NMe>QN;4LkxCucS4ZD8e4@QUx5E>$1d@S z^@5Hu2&c4Ag>S}-JX@B@$KDGrA|mFJ)kJfCUjP5wR^{35!_Z{rBFD3*fY|il4z3yc;bVMrY8$4*cuB^}JNXo=Y}o zuMXDfsMSAygY@Kua(a+ro^eb7ku1KXv-4 zS*yQ%Q}l*mt81~odjTV!@#cAgaB$px<+8p*??N|S-*LWUhVXzivQjUvbcW-uiT@zQ zIUz3|7asXVzx`@xjpa0Ug$YeTz|qJ|@wA-ARSo!Q?}JU7UjJaYgvvsuKN4MynG`;J zqMi|Yed~l7@m0{|Ov+2}!#}UWze|?MWY#g!udiE|6@`nfY+7C299W2z-?<8BeB_2j z`Ty#6|i)bqEc*cBo|oa+4t*JoMz&VIVvBI-0w(ZJ}N8QFd9tG~S%Fgz^2 z-3tA@1D9W>eTq*CIor94pvKA}=}Yd^J~*;yFns-`uv=WUEOY~Z#HF$V2|1fA32W*? z@QPL`8Nx3Dp557YmPTXmm++wk;P5C@IGCVXbaCq04^=7B$T_P+>zyTY1L$77!@kcjAi z)*0Q=O?L+#;+I@H!Xa!)l1=KZPqKy9sM7ha1~K4y=xh=gCP1lh))K9-B7t>>_O){f zk?*1Nd*-=#pDn5eJj)aQvdM`$rVbOnvN(~fU5Tb{h|w-pY=h$sYnj!A;L;c=BT2$! zIGAGk5K9ZE_{Tl?R^rBP8y?^wH)NqN&gz5ZbC+l>?kfN znRIt||8D75U)kNebxR?nkz2X=HaoC z$kAHkOp_ia3GCSKW@$&({{17(2N!M{O(w19F?ylfuqzO}N1mCZqfA3ijRLoM^F&$_ z98pa@rjGh+lYdaqYCO(=cD3THGj^oay;34zc(5ol^^df40B zTfn~x!P#jVD}2639wkqgh4>y!tgfjUb(}x{jg(sir#Au#xtJ`8kUj;K6pQ?Qec#$% z>5hOf(oX>QX^eB$A_K+`trxPEB)O)BLOKvG}e>I?Cv; za{Re@0C4%wJ1WZ7j0cMt+4c~Or;&_#ms)e!{U{SQQDF%(q50ioYUl&G>fECXu|vU` ziV)Qva@FLz_(#rFED$x8fKm=+#+QWPxmdv7MOa8d%Mwg1>5BkejcSeybGDWW`0#BC zlsMsbYT)~R#kQnlY?vD+w9R~bx4d4*%V1-@hgg0tCN zKLF9#68#V>HcxrGaM52++YVI2C^i{NpQq)JDCu(@10K6@4h&Ed-@XfH4@i)5k-ZKh zK{G`3cn=Z_NscT*Y{w?8vlxSa6iHsO^zY|pC*wfaYWJ>*y;i~B1wB)>RK52-#;9dpT#=?82sNVQuilOZ z&E_*vgc)`1G-Fp|qKHza{)oeGS86HpuWA^AE1}?gGCD(e91n_glI!|+$B@3vkUH%j z@c*wfMU(^#9GNaO%Z?rrK7+t9OSvO_AsZD!zqED_(jr=6+|iztWt4$3aAWt}g76%*vCC;A z577zlfI(vYF;Y;R-+PYc@I@dba#9?dYp>dH0rW$o#@nq@h6LJsL2Hda2aWja3g~lB zOCdc`MFp_z3JFF_Lg~OHfmo02$uygnIlrLnHlKnaXR6IoWz=YF;mWejtKh1i;oH;H z4S2aZ8GSTY*Vi!WInAm+p=_>7mx#SEP@2thd<7&Ux2W_TbP>Cv9w!Z7z(W?>lOu&Y zFon!Z1ThlVCXhR`SJcC9?#*pIMO3;L=S0UM@Sfc+^%0_T`@6+jj8^+7i4~)I1cui^^q;JR#xGOYRbzwSg?V_TY|EiJn!I}SHQ`7caMpx zNq1(}Qy3?Kh&Ec`6XGCv9S?HNIy0I!XiFPySWueMy`1IR#iqleSE&MWAk{6Gx$&=y zw`xLa?ED!|62npfrR0PNec{Xw(c=w}ux4NiUTXBn#Clrrm1WUn1pp>HOtZSHvjv4% ztsjKhTpY?p-9ta=K?Ku8;&gyE7JRdsA!&pY^Rv4>`(&!G zkVET0c*??Ja;rDcv+eK(K3|t88rB_h(Ew}T%z-6M!C84c<3W82)s6&Yc)bwbn_fPp z0XLNq@?9*Bcn4J~Go^rBzb+aTTQlZnBUsQqBD$R>v#v|-F%c9jE)U6wm?bLP+2WfbTC%fj&Ag~fO4@DjHX#lmz|HJw!s;Mg>!2vj=M&w1L~Ily*1ATQpe2Xq^qYabA?f8EcwZyd^jO^Gj( zBZ&oRtQt)cM#Bi|qQ|?1qgYMZu!bSI4e#hqIFs$N4y}FB9mrS$sy@8zeAJ16cEB{| z54MtW_)5t=Cfk5GRo^O)MUw?+_Pwy#4+L#$xu6FFR|T|+ZI|f|=CBjt;=2qzc{Tt} z`mr!t59(}Yp++aq3?{z&e^nc9%8ko zF!qF|8_;OwgVq?f{$SYWNH7E=;;fz6qCF|{Pb>Y6rY%D+T-+_=s>ASfE9>L9wmGdu zIfwV34Cc@dorU{mi96FpK3#cOlwteY)dSTcH1$_8j@zj&Yx)ifD6h%L+Dz4FP>NL& z!4+mTU*3TB>}lzOTZJx@%U%cTXXV*#XwS-h{1DqaT0rjr=7FRfE&3T z*Nmo_7}{u+B9#eJF_0|XMGi%qm^xubFm}RqE9;#!od22zrm3F%XX0Ua%N}8!vk0j1 zFkXm3#s+@?JoW>O5k#jvPG~=y+y^s837}h(Exj%gKY|BoXAR56)i3iYsBAb*#~woz zZY75|O=OgTlRU*htsbcf#+QV3I-LlZb>2=sBsuM(Qw~F=FOb|xy@*aVT5vD;#{u<2 zrgO?f@loZTayFuiG$h7@TFukZfqFvaF+MrG3lwS8NSS?GY>$zgm`c^y>|06w*8(!Z z@Kc4?zVDwF0}Z3VIfXr~XpYWDeUg>cVr|RKm}a)5f2O2SK2?E3(*ydDhgEQ<7_0>r z?}Om>yD9F6HCe$~A-}W}$Woxt6UK5m>YS^HpUbT0vFokStp$Y!A{ITMFIG(a@U-4tficT`eveb#h9by3Y@|#e0y-V zB=?6#HO18e<4!oH0dM_s^t`LyR3c7?Sy{yG!r{R2&xJX3%1#LJzG7i20;r^OEeX~r zsVz$LL9}AA;~0BoGspqm&&qc@Rt6uJJBOhD;%G8Bl>P~>vrTh+{gU|lXRE+16-wQ0 zugr$!e|y6F;v=Y8=)hfL|oj<3-+WN%f|tg33fFHO{K6 zm9ELc-+1U#92Q=t?OKh|_Xy?e>8FdUYf$6W?~U~-nxHGN?O5H zgJAp@P(i0S4&Z9TgwdeOaXIQlLrs8d5Bv>zlzIy&*5FJ@H({6N+L9^VQ_Ux&Hn}<4 z_=1BU5p>~Y96?reYz#~{F0`mGXb*3_fM@Qzk#^ampulzwh6li1Ud!|1XGR zIVzJm8eXA+mk4b7C*`k*d<`2d!uZ$-RUn^hd^D`UPsaMNZ^=aT_cUK>mlbaZ=Pvt5 zZAmBAUyX~-Bt3!7hB`=NBYn?rGRCHv~EjCS%0U{YA^7kN= zOb=v8!LR~eT-Y?A>NffA&0N>0sQ#0~!*L@IPp-@c=g)tC_DND&5Z~Jp=|QJ7dJ{^Y zu)=L)=K~uxDs$|^~J{)M|NhBI^nPsjy~O8NbPN(yUkoxjpdWQwDvr=J^+1B zLMIH$M5;PZ5|Sul&>0JP$W4hzIr`Fs&o}KC?pD;k4Ye7CL(xJ?#Xs`GOF10yiUt#8 z_t4R8$$Hi1CFj2_J`^OiIluij52vS4P2PG63B_#qXogylNgn%Hi06|JpLh14W=?;O75GhF| zq$bi5qhXBg{CK{Hwmzj~D5d;D;>+5OXKR@<@Kwuqu z+VgLp!TE^u1I;rHBMlI!I-cq55$yaogO{F_4+zA>_TL9we!1<{`AIroeIp&Zi3=Ba zIN6gQHAkJF;`PZ1+<{ipK(Jl4kD_>UAAAP@eI!+$%;@dB!L;wqz6Y8Aj3E5V4 zXB;&v(Z3UfC$~Rh$ zCr|ht7Pq_z^&Llq(?)8!KVdTiRqO9S+(5N81_cjRl5P+vM1TxXx7|?Xm4`=|$&L92 z)Y&$_n(^@HKY%dM1ML2)JwB`N@*|h8u3w@Oo=qRU0Q_bf9_P&M`wgG^%=~GgzPva5 z;}96f96C@sJ!lgMJEUOL&H(zCTo6H*K+y$8F$YJxl(e%Xhdn9*I0h)kKnG?=xRvt% z13VM%*#uC;GpfJ)Dlq17YN*~aJnut9^$gW#S_GX@2QVZ#3@}Xb0cOdVKRG87^MEn% z1337#ilOEJtBpkWV?_eyu#!6NnFDO&$;5d;5g5emQ}NVFVB;A7*Md^g=q?P9&n*6X zNGkqBjv8|etYc1daDXxOJ+S1yfkT}UJkf;s)p@m@nXN|j@ry$vas#lzN)~)}n>vR% zCZNcCX=)XVmMD@KfLSGw{Bg$t#{%uUq;}IHdl92z=Exz%_OLf5w?qDfKJHk&M0?Dw zm;J3QgasbQAW3svbe5d6M+GM0azYj`BnN8RG0+93N3)4eZAI`+( zxfLb+B@jRfauJ8N3@gtsf-Vp2(j+j&)n6Jd!tQC8Uk$fbDcNH1PmVOH#s2qi|5@ z8&4ySi3?eu1I+LS)oO!9pN!p@NJpgHm`06+FH-L|rm7D<1$PLe23q&`k$yMZ@x@8O*%%V{7%mMhr-S&m?G5LzLP6CP= zi&-SR24XO~a%g}F-68iH@WE^kqsX8(0!R$+DI1AG|F=>Y%HT98&6q%;bsQ~ePc(CV zF^|$iQUOw<1#TSmuUqOdz}-47y#YGCr!}J{V47AaS%qg} zcjKuAz*>gM>T3s;a1U70nmLz@Vd7=nZ)E9dnNN0TuN48vIi;zZ;ODRn%`&OwQ$OF` zXUFRM!UkzMLX@s!7&aFPWz1+%lmQ(@V76n@NGW9XZ2cC5=Lz2a#tifP7Wiy9TOHjkzDw0j7)mElR6`q=sabx$(Si?aM zIw9GbjSO!1@c_fm*-b}1?Yq^I=4v|p+mAt?Q9+r0l%F}L4+vo(HXb;r`sZIr;mB1a z0%;=aSnJ8%rAD-#DeIMvCvO@`NQF5Z#Gpqr*noFAyZ@-o)P$;&B!H!3%$&pVWE-#n zoZ_jJ|G6Je&75#Yq12>mmiC?hd6JB7u;irAkTLnW`WD3)SXZZPO%MH0cQeYjIz5n4 zMXGu71r=zF<|5w7w!a9*1o$IKKdZJ&B`4m>L3>sG)(Nln7&85okA+VosCkP0$9Rh8 zxw)Re!Ap=&JKHO2Sg`zyS~$*OAWx{&`ENu~>m5cA+@w(O8IMQ@Z8T;t=S+eiDLSyL z_@5P$@q3kLG6cX3GwyIm^(5I)&u!{Nof?k^l+{;PTN!Z1T+$Mk=&AGgV zk`fMW?yda@7?^?j97OxYr?VXu1^0wLex9<(M6@?4gE7F}>=uEUc-0@L{yG5t6Uf#r z105HzfFu=%ClZ4If8g9P3Mf(k;!i8lz#@7!J_vY>*^Ea4iJRSLm3wJe<5J zaU5a6tjAHu5+=&u8GfzE*Sr;qAw#tv_WR>#A4z=2bP;wGTY-%KT-p|=68dNSsVNb2 z!U`;SB6n|z05jAE{1)YZ!P2vn_y*lfyRpzJOew$|knx*xJ<|7igA=zNhe z1<%!nd~VBE-$5qb_%UBuXIdubNpscyf>OIlvg>;+FW2s-ah~Ns7j*TG=~z4E&OZ(^ zH9XFjt)_KM7-Gm(mxTKRa?KFE4_uC=f`&?7n}=Rf72U)^#e|*0zaJmfa)dhw=?2yE zv}Z2o&N0Xh0-TsX#=-ly=uhI$64m!8tRxZjB?>FGL;b{Pjk2u--^ogOxoLdDpuWI- zyX;#$>6-e=mL=Bv!T?k<>+#0zIQ0Z)eG^r8inN=C$Hx$W;k~CqT%em z1XJ<4IJw_|H{XdWzSDD;;?0kM$$AVJJ2KPDKcbDs|EWBYnFp5ti+Wd<)DI%4Bgg7H zbkz1^_1|=7*hZrIAKkM^#V#wvo-IE@i z0@H&8eV*d!WT(ddoQ1T?emb$-UL1fX6kmuH;=5`6>ivs24Ko+_AyTZlYPhe1I4x8k z^hnk6fq}N1^?ONwU^mnjnn>3AYXvwly^xVR_u} zF-eVxKQP1}Y~8A=x`KxCk*A4@J-?p5I-`|W-;AJCDYAi$+M{anT*^MKB1!YA%I_Sk zj!=?LKRw4y*b&x=BeF`_nY*khFCBozeakp*s?jC$rD9Z^)Qc~mYW&XC!Pv1}qYQD3 z^fL^kN7g;wyZX|0tQr_J!~#;w*T^?&2-^^*p692E!u0Y=-I8|~%*mq{%`b1|T+Z@~ zzr^kcf=|(8j!2w zD-z$sqyxW(Dy^9PI(ol+VsaFG*=R#sog}p3W19dQ6Ffv?Ajzn(r|r9KxXI%{XwUIkHesJx^0=u8sMVZ?!f_GjD2|7vWgmQT)u^H$v@M#4LaNUYKq2Ei#C0h`K?-A z7SyzQys*lBXEC6?&*+}hOE!h{k`S8qj!95lm=MUh0SY!;&}xt70c&zG#`b|(*=QPF zeyIEg7rH~*G%+3fDEzs(zq8+Z31aJ=?RtqO7;k|%Ccgjk-*)>o7k2lc9~W!wo?JrQ zXu>mYd4PUvjdzATm7Rq+mpVe7NFN6aoOS501$OI#;)*s&*6~J+^Uv$Y9T;}MC>9AS zjYy^DvAtLr6TL@0ws~X`n%dQ7L)e$`&Ym0{N@DxVy-hQ%jMl<)oFMcRN*@cYP8cbh5?r?efTI*Ll>Tq_`vXfO3&9;OnrnNR0}p^xx6nVWmu6yqs!Hddk) zJz%|geD>~8oktP}rE^@$ovBEnH!+^LjHO9Ju$CT~@xTSdA$-)H!6fw%G0bl>mqwSd zPZq|hOZ$+*1W(-9sW6(e@tH=+?A+(N4L60!U?xuJS94-b>UMYy!|2{M>>D#=JY&Zx z4)AoDK>x0m=f@| zw7u+j%cJ?C=DCy){^fDkd=g-ZeBYJj^j~M~N1Q8pPD8BY38zn3~=EZxWgqZVs0#Z`!Rk)I>scZexiWm z7_^(bO3!7K^Gfx%m+HOd9odM?+@f7~s1EvTz2IoE%VPWx4zB4hW z*+lKN*6@e>Oui)eU~8&lW#tnbrxQS{wW@EZ>m-hTnOnrOOx0b^$htlVPS_o$zdBvuY7;$_03>`IYvIk z>l4*XY!hp9rJGw8sT7;ImjlYF_FLM;7TuACpDPYs-p<~GPbX}z-1Zpro_Vd+n)bAr z_`e!FCp>`S+0A<3x_>Ft*A!u1K;xKLQl@iU;@zIf(j6gc?k(gku;HKlm5Iyvx8Ba= z@eV2mI;qTR!p(9uyyaNGw~{q+S@L_!XvKNi+4~-*F$m!wL8Z|2zP*!X57rs-Qjm#$ z_1O%9tEH$pt4@TLEz>(!CXdz&l7#E)_a6@Pw0$W}Uc%HVNa~odJh6Dz=M9R8G)ukQ z5yw+LGJij|{|-IbIu11ETvU?V`?L*GORB-&EPRYM^Ox`GF-y&nj=28mEV7|b%=lvU82ejvTL6ANRq*K=2 zljEU}lAuF}2vQ#bc}vfiqm?4-)f-kTIGQiZ(|UN9f~V?#T-y^e23e^W5(QRJC5_f1 za-SO=KPUNT9aylOz8YfUwpinv!u3N6%?|rzX+oJlUr9&_E*s`*x22Omxo!+)x{%lT#l}EWGDcG;YeIxojLZ_vl=ScZQ?u-7F7>5&8vfPiilsWeBq6~AHx1|R ztOECyh+4kX?!=4Fu9clWy8gh;NT1e2FU_lbg)Z8Lki2OBP$th|qn5qYrqllG-_%e3 zIT8esjUDY;`oK`kDjI==JnZp2U{tCRbyx;zHXYr7ZD8d-eR;AO?(JFU3f`4UT{;!d z(QM{XfL*M+kzP0Pn2AlOpoUjoj6=5Y>9?GvoW8pg@2aCcEF8<*d7+LD{C$4xbHSy;V#a*LXT$eSI%4iA}HMuU&#IHh9x2gXxdb{uc_Kv%r9H&p7(-$DH{( z%O{twK(@S}Kh z?E>qE$oFdm_d)lYrmo@b5xq|fIW1?WbnmJ^8+WW+wDy}pl9zHh>VhGVdwleVjFa~N z5V}Z5w}8*K6v*P8@|(#msf!GPOvx17z^2@CAS}7XLHPMCO#WCBsvL}sv{p8LBF6@f z2oNlz_1aOi=4EW#Yt#H1&6p9Hd}JJH*eQ#IbROzdm@$BEyJ5B>mrTB*)x9bx4~dX_HK_L+UIrW@80PbKV3UCe&a1S7G{gW{y@Y_YbaIb zMSfNa9X>uo{uL3_-~PE~6S>RpV(#BC&a7mY_p>=TzoSW7gaN)$NF-)VPU(%`i)w3* zZCg2ICNX6!Ho+nd>$5o`CWY4l(ETIIDcpv6Gt+?xYF3DBuZ!3D{H_y+h$C(Ymzh=J zuP4^?PpUH{xTV;GpLDz|?@8l@8}qmn*i9N+c`2A0I2nt%meZ|fCTZxDtK2U%NOpa0 zCd0j_42k@jyOz@X;6OsF$T0ECQtu-oc z8o`q+GYCDZu8$@T1owYC9g+FlSlO?>nrMZU*5d^kU3T9R$ESe*l?q2>3Evp#q$xRj zFKQ~lQD50RE`z-d?ynZkH_tIB%@#an1uysYzi@i6tw}KhC6bYqnGt~`yK0e$%zx|) z8B@N1z;R`|uqXycE+%@8LT0=Nk1A6Q&+1qP(tDphV>Sq40J6%p)o?({>Ul_>*sBlJ zbjQuD7VtQ)N+aNEFhQZ(bai(^Ctb!HdNwDLizdWrmlkde^=R?4eWvFih@6Ln`}m3 z)@+eQSJg$pd7oSNbJS0dY`#By&GGHioyNM>pu#o^{?le9sD`QAPWn&;i`Ze@*rv>b zlHeHn3%qPLSMTL|SOsW4#c&rY&)a}{MMr|mC!GU}lqmjZafDq;&S??#vuBPcNfy6M zR^N^%FV183J*k5P9H!LsuzWuMD$QF*XDO`M##md;71^dck>oaE6VspoCmoL+624{ju-UtQNn7~N#hH2W_SdP&14d0UFYgK467}}$NM)d zUa&Z8HG#M8q#Rw^VD+^Xnf#<;4F7ebs-zWFBg!(;vnUN!@cGZRVlcw9as`{s8B{zjaXvmWiNbeP1lkLjb zU?v3vLdyP*pyrqM(UP}qK<5{;G;@7=h^~Hz(S(_I^)DBEB`oTW`_z#+`Ki>+$89*R z>6WnsSu5ZYVKJ!nNsWJsHetYnkaPfh(23s~#{B(NvHWFnC_R|#Jf-6bd0VBp;G*?8 zuVs0VvnCB6V?<@ zW9NaV_+Oq!N91#a2lNp!?+Hta0e}GT4Zlk!{AoOqosT1baX8+<0dwd^!WOeFjrc0g zZ<)|k6!h%cIUT^_Vy*^f3-<83*)^bLYcPN*)Vh!MA z`)4KH-xMvLo-Dqh4EaV8PVmSY=RS2v!~SxkV{0Fw9d@!3HkBH-xR!rZQpqs}V{uzr z`H;q@UCjeGm4Ss%g7JCdu?MytOc$(@U_E15VHNv7rrc|J+@WFDs2+r^mbTc#AFEf6 zYvh^s4mEhhY<|9$nr%29_^yvxe$WarP5*@iYxZ;lwPJNbrMt4Y?#9=^IXJ~HXM{Vl z)2o&4@>0Vw3o~K@GwP@3=$$|tN2@TU^5)m|;r^|W#on(b^R375Nd z2igt>X3fI7$0}j>M$0N*WvxpNdIMnrFiZ%$5^lqvQb9zW4hE3hfR=muIDzHqRa*z= z^$AF61G?GbMqXBdyiQKOHS&SWq$7oj2Z+GaYgmYm=xhV#r|_X}TZoo!Fr^YWBw*fn z?l8|Qd5g0JGvoR^4{a#so-02o^w5&_=(As#qa4Z|#aVUiSd;OAw806@$&i(!Sgt>T zac7l8mEOng>rbq{+|nG(x9B(YUNTz?91JNG30yud9>2=f2C{P1sgeVwaB1R!6wNBj@sS7c`@cgs6 ze|m~pHCAtFN0~@;4n>0oi2LL-HP46ZjsNLZij3U`UU20Pb(c!oSxIkz-|8onoc+}^ zWzWh@9f1)B>VP8UU&29cU>QR1;rdu<#xJ6BfrW*Vdi)ONJc>vgF^EkDYFA3a%$oC2 zh={)X(4jZFF$o+*S~gA?NE{Yh^zFOozC!4g~6mD&EO`o|dv=Rdzm|e(-zlOz$4rw6_^0;5tjV}THg9RH+bYI zbk~sjpCC!)8P!w-akLrG=ah9r3dhHNzNZ~yqN_01dO(guWUJ#QCOp#amYYbRIL+0` zg)U*K$5&UYak@WCLbVP4g?+O6dwA<6Zvqy9%tm~NTuG3%yy|XMKIb1wa#n^wHg>b{ z`~K7maHb?fJdX*A_#xJ7%g0XrS;tRh>>f8Hfy4D>kWHsSE=iz_rc}cR6+zq)n#W{# z?z_(e_wYZ62h`&eRdl?fn81ko8tdtsdC;G(BoOn!-VDVLKNs}KE;pZ@0TR=ZBO)&; zP=uq&cfYswp8LXAcb_Gub+q0Z!!{$47n&d;v!D`W_fyKiq9r-zgW#uc{jDrMy|NP~ zLvLVGQd4Jfp!*H2VUCZ6nd?1V{t>yCd)3`~V8rvul;W?(#LuGl?QX^54_wNT2aNKN zentTDLZhCi@6dc|()yq+$nS;k)dUO&a;z@1E0}R+??RY63HLSUyK5bz`)Mt;Onrmm zu$Oiw?#U0f`{VDD%+kP$a#BVAC)A<~OeJ}R!h+*rCTwMNJ%U)t z^rX;`!gr*V+w{Axq@n~jF0n7w=8xY8Y(3L3fYT-+P3N7!Rv%K49IjYBrM_-!wnZVm z3Aun8!6TinFZiJ5qdR?Hh-1~vAm42Xu$Xlx1MR!igg&Q(Ahn3Nq*C}!zHu3ZX>XO< zjM>iM90E=j|5JdB)=(zc|4Dy@wIpyo+w{2>PL1+RjX3l|XjBQ;dtK4$W^B>y5z zG!v&c$g}$mvRPz(&*nBy&63&R5Un-sqzhM0X}Az{%<)edh&41w=xxlBFi95&#QdiQ z8}&ZVQwN5Def1#VhC72i>GGwa(5IO`{K=pl(`yF4H9555-pwbj5W*^je{)#+z_ zNt?vusd%fXB0`R&O>S*DixCDyXAKiPZEB6U?}sO5WFHte$6sGjUO7FzvM62N6e@&l z7(0XvC^t&J>{yzWkZprx+%zUX)2QLGif1~Ppe1s(7mlV4m%ltco_548sxiP@dQ~`jmc;6{5=%cH=|~7*RhMbdVH?(_FvZ15N%S19eAo&-CR&`RFp=YRg!%_PnOg z1zqRS&rf&~lk~GfpnBRLwmsl(8E?@Ed?x3V4gY8sIzGq~nlKjtUk(g+vU2k6 zFrBy^^RjluROS<0svskaHf+tst}D2Rmm0NbjtTWr|{83QQkM^y{e7L~D3o zwq?Q=+kvrhb6XPNrJ>+~bE^2@MtR(MnPDL4H7WtQ=k~G?Oy}-oDn>`4l`p%%Zzgt! zhN;-(8mqCS$%XL1;?R4!;y}Cl4r_1>x*9Q!tjaew9xZ|Os%cGx`fToh&OM1%W04I0 zQt>Q7c~Usva*z%2c! zgI<5Oe5l)yD{*65~y8mwY z&n>q-@_#C12F$dz$nmA@^jNXPfT^$KBbp1H7xk!k7}s&i;)t4?<;zy9cgbJP|MJxL zD=QK%Gu^jroMcfjeI6dH92oZ^v}FcVBQBDX1nRD+pHY}jde<-%G&ZZ4me^id&o&is zi0MSh>NUSN?E7L)N|7k_Jw|aLEg7c+n5fY%3skgL6#W-|8J@4JHkb`h%8CU2#&nIK zq?sCBRj!?yWZGH^LFUv?KODEYohX#FhbzkUS#0 z?pM0Fy}WDzzMkOvkI8Fu#t`Bs;Xq+!*H24tD^2Ggubb7<_-FUH?#CXnJxsqn7{K{z zGI2;*;w(=5A&yv@xo?%RHVtzoCX&W53=UfnBE4>-f8(_p7^8f=-jap z=E?6R&0_?8-k~6j<%8E0Ba68;ib(Dx?mf85k0aLqdQub(xC6PbsL*vor0)fM+D636 z%Rgc}=B*YHljKqJfYU1(hQBo%9q8otK%bMkTsZu7cZ=PsCE73Xy zAT4DE!+hhB5(5KJ;iSAGdx|a+HrmSp4L7#F7yy{!01KKS(;d|g&q&w z^474yZBM;ww_99M#Xjl~QAQLmzBf50Q!%pb^+;nprq|$n{teESBNY!*4jt`Icn22} zT6Q$*|GK1S!v~^EGaxLih{ql*ksS0;NxDBO-oltshXdZW=O%GdGMaBvS=$@yHFTi` zLuPaQFob&TrwwD7_b)6D8Q_x=C+CUESQE}a2Nrpqqy8xti(_2BPeXcMMmG?0zJTZm zpVJ|Sf*#Z0|L?&=N+6GBEO{Wr+_aCZ2gEkdq3}C;{^wbuE(#9zJKcA z2nqw-dBwI2o6a$^m-&fY_`vm7XVzKR1AWjheeKeZ;BRhUoLj>^LPB-mb!*9E9Gb(| zp2=Erq%X{%XFSQNybF)dV3-bvou?tbt&go(S1&sAKUkGfW}oN!+ufm@qnS<1oC}H< zPF8;vG74~0QuZV5*}wmLc%`g#yui8=Z36~Ij@zD^yT&&fiy4wF@1}o%EffzVr*S!J zq;G)XgExp-%i?ql>qD!#q#SR0)3SUOi-GB!m8%`An8n*s<0b!&1U>N^bZnCsk`Y^+-Ob}TfHUC6L}RS^KSEad**2Fm17Q7?orCRoJi4w3u14zcs3l{C(e0o`-7kiFpB3MmE9$pG-&t)r1A&I2a zO;THsHs^FxEVKP3xW@71At*Q?{s(Pt{SzUv6Khcg>nAo#(oAFTI$)7LGSB2{>PT;4 zU8f-9&i7SHLrPM-klTjo`M*AB6UFls4x!Cn7Zz5?e5D|pfc^%Fh1OK3j=YLt3Bxf@ ztB-EVH3>2&Ax}cQP#@do&s}N-DAF6ufC>8zfq+i*X#Z;SAd{nXz#PK+w@1poMe=QF zjTstfGsKpI`Q;ty-BXpfZw)%LStb+qkIz&ISaFA3Z$6V~6Ic>!$mI=oJzT3MHDH*C zYaj{dzpAT3|9+RYK`&Sd3N1Mu*;G&rd?2NlSjhZJ?fLgojRNbiw=23RyBM=cftUt-v$ z72?)!N{U*knA^D6EI19d)p)-D*IxErRcb+c(K&OzGV3r3{~O4Wg~Fz5=2A%3?IJeW zM0TZ;=CR>$f17uCV@biRp>|=?<>@20!GBIcL-p}q99JeZ=Jf+uHy#;PsF_<==W16z z^16`g;-5^1l zuq%V^&R8rPsTx5bcf@algrRKINcBJQuLq(e-90^&bF1W$U3*i+TSf7QCXw`#<+1{L zCt_tR3u9OF9xXV9isd&@@1%D5)h_e$<>>BMEMl$^h;2~X4R8~6)!#%=&{yXDx!$L@ z@9f>%n2EBGwqlOGBn#^kF?xW5L=zUb!`SP^q34C(;HMAY6A5V%M?zj$9g}d5$G>cc z`2xS0dmpIx*GF~2G~iaqsnuHY#NXLx)`4cduj7C9S|LedSOs=h&geFwRH@~EK=h2_ z!+C1a52*j^G!d|TVUrAYbP0ascn|f%Ger9+bH6XJim(5R727Bx=WpVLR{!;Dt1gj2 z8+_1zV`<7~jo{7fr52qisY%Y)I6lYCS4U^C$eZ_-tRO=H_2#lv^KZ9%X7>xs6l#no z-uPXeet59LK$G_L_9*MB;ZZ*7G{WiZe+{3p_IYN=5y*7=NXztb)>e7D)9tXb@;}9@ z(^l6X1j&t$U3x6Q_oB}x=jhf7L|j;WpoVAZROT2MLNzvr#8S&@(Y0eX8K2&}+%!du zmL*W1QO{fLGCUJJlZ5{$kxBgRVj=W=WwC210m250c^{t^R)gLgfjJJ|n8=eQ(})Jv zww$>a3?4jVzT+qo{M>VT`dPt)u+*yggi>J3V(5=`%}f@)bM8=$rnRlX$CQ<9`{OWV zdY_%I-mHu+CeXrp*y+Xms78v&d(B_<1MPxYwP#-X2TyuH8qYRxSEjz>^y@ zIpQxjTLiYOB7#G_Ha?ujt}8Tggdp2*hS0>!=he z#V^?48+${nGniH+C1xQg-yAGPmvy60W7u^^D>%^KDLF=bop17iZ@<$`!@82kTdX75 zS~gbKI*orDWx=7m@<+0Vd$+-e+}j~bp#|u`NvFQLE!Y6WxqRSitQMd0izK6HcAYv) z*3^!Ob|`p3r@TgtB-Gg^=zC}(=#l%oSU~KIOX7*C^R!O;h*DMol=4mSk)7a!g#yt# zSwjvB!z1R?mF3E7G|h@_2L0k|XgUOt)?}E#x#FgL3iPep(*@&O{BsadQgzc?Z|;NX zHfC?#?hl}OH_=p5C{(3gv8QUVw>}`iZ=`p1%Rqip;sebP>Od$$0QtfSY6HQ@>iwpN z%O?H>=CR{Jvmwl~H{X7np;&PaJC!mckxlh|H<_FOG>^PtA4K2A7qM|s{;JD4vfe$g zvpO3qXG%~tN6g-!SHytqk}^On6Jg=MdwnLr;TR5+7bFQYaOvMeI4Jl11wT_R*4Wgj zIB?AKFDiQIug~7cPA7Id#bz3|@40=yt9`MCWmF;;UKNIt)#teaCjn#S#^By;B7a$B zJUKss#`ad}PZ$SKE2>kfMw1X6fBM1)rnt`I!ovlAyLXMnB|Vr9xubgC7`S%}GJwlL zf&T>ItFNf(G}l}iy_srTkDTagI1H%o;o~P3PlOiaTh1IgsEcEr!*4!pMOB;D$>my> zOQZi8RV48!{W-mKS7L`(X=zyvtMNuvutd8!CW~2+t64ty;R#` zCDShkMTcY7r_jUROEWZF`WjqNqXRIDW0Tnzt2jICmox&`^!RF>bKcl1%SuOEHinjO ztIv;+i4SlKbEpBDDxw79#|M+7j-KLBa1c@N)^dTDAk%>5#9+H*le%G05l4LH8k9s7FACSc1yr$JK4 zZP?r3O6q}(t=(&Xu(y;&ZZ+Xp@`GA4+F){~s(NuA(fi5LO~iiu#xKH48v4DIkddJ^ z4(vl4oyg~)QC?C~#e@dG!ka_A4JOF3v%Wx)8vnUvfoCnc@9^=bqw zI1_3w(G&#td@_IX9W3UR`*f6DGx^`PGcva$gh7MLQN#1vo6hE7R#5ezi1Cy|$hj)) zXz2z~s93%qKWF^$uP#2lJ34V3B zZ>5tF?ArXas_@?kkP$N+L61Cn4TqMnm(fpa-*S=fr_nRb?_aSq{1-QQNuG2TF%jNr z#X!rb>vf?bVlfxWKDcSQ%4XdeD{U#%77u>|1|dRVxLES`1-;>rQW;f$A?*I_sdL%| zSs=-{>pl&Q4FEMDyMymIJuIyF6=D8IRb-BLsr@2|y1KO#FBy^5$GKNsKVA3|;w-BmFRZ!VqCtyD z*XW{G834n}-#Hm}#W=4Ec6NEfZNFD-cqy3n(aPd_uSiut6*)wuS zP4^$tPsZmtxgdgmU2J%w&#oX+-^+^c`0xTv8XNRV?pm7YBXCCptK-R4{N(lD~NMICATpV3;jA&E)+O!p-|SdYnHMf)hU<-s0ghtUa1S zbeFnM%1zgcBX9-w-yT{D$MOuPU6XBezC6A71zjZd(pfFq zE!RoqU4mDua%)*k{9~;4s`;tOSoF=wUo<2BJ)qb{26ZD>{3dP{np*s!|M3>5?2m7v zKGVfUWttP>rK4jyOkB{4xn<=S{_Jm!=<`3Dp2OkFd!_Je9lyT*daDb)!>>RHs{%>h zL>5DJQWu{N>|d*>pQ?F342=uUK$_Aw?^O9U`6*v*zIZOIJO#&i*||Qsja?qAkXS{p zPo;r-VLd;9mINOitO_@A6~rl+XXgmwc(S93KjRp>cB;APR<}4J!!OX?p2MKdR;7td zgbsgR`C4n@!Yd3G`r{bnr3rc=t(T$Bt~63S?w2aL90pAYn$Fjur9*1p_vKnRe8E5m zR+K~zJ-eUznHj+hbBwV@pTBZbKR@j*9Q)mS_lE7{jXkXw|D@i1ZEyleR*3mg$~!tX zO7V5v{m?S3HhV8LSyALeX3+452Ln#IZZ%zD7W29eCj8kZ0u&wP$W(?0H>H^2FO@&BOe{4e?>io+en zm45FDBN&i7|IjPTjHDYPHK)K`6YT*bn%|J!-fXwYE*=YZkBmlT4&EVm>FDLz%9cky zuhh`6fF6CO7wDkdJL7=wswcSQj9g=42o44h=f0yg`uR~(?2*nO|80~`G0Dm0A{=aT zU_f)B*agoKQl{s&?xD;OWBMrn8RzEO_#o+0Hjo;=>d&>V5B}v)^;OwmO6H=JL5i^V zfbPwlYSYgWUfR|6;48oi1ANOT7B`uyigSx8+*>^<&VH`dLb{~*<&|s?1(;h6aJP)f zes%9adgsISek;W)CFMN0AW~%DUT$*Jk77~nGA7p$mMDrM=3{Q2xI%iLRvmO}E%?bk z?F1;uG6ZZri{{b@fQ5*BH~(O~xULNcF)mA>C0aF`rY|UMU;d-xHLwTyz*6+cMM7vT zI~!ULb=~rkVEAvO|p82^T6ZZ2PY-kaE+(jG`!K10(?lY z*d&PW;QKXP?R|ywa;pEa4c9d+=#pd5CG^{lej@m`T2!PUd|eK-5LB%;i7&pg@!V!2 zQ?vIt(mF+XSd%8w33xuPfEf7XSz2zO)abj^=QO){QUqFB|G>nyXm5G(GDFiOL{|P3 z*Y17EN`WU?54$rPj?L+87g)bOLBxHy@x&pEvshXqFN#6@96BkeF3@uDe`t`XqDg_; zWZlEH3(*KYMVj7Nmb?TiH__Pau)I8dYyNNWghpJ!o~LHF6Bs1&YIti?LDFYSnITZ7 zWMJ$TFT7Nd^Wx|_La^gmV(^mNftEry3j47JbanK)@54PNr322Kg7um*Z8RiDAteN+m#%*Q<&yaY zjHGre_OcvT<}(Gl?kV2+!Dw5;W$C8_)4}18LJQV1^*23@))9ix?F;ZsxAgZr9D#X% zIQ*Hl@8yGc=N#ANeJ`w?up*+}MspP~PsWEt!oon2C3nRmYv%KXGq-8rTReb=n|e<8 z(<{N5&#@~qYzjZCuk%in8q6Xktyiqu8b0Pbswz{<5F)zedy47oe3Bq1{6_7>RC!@x zdTDS&S(o>YODg*M4k8ULc?*pS3G*trKbbUZ+an_@4Ei!rFJ__f{QZ%FznI||_1nt4 zX9s)l{ZuY-o?KBa?jCDa$0^`21C>m$phv15Okbxmblg~Hq)!g4x+G~~-rim#R)^{2 zvb4U|1u%=2zxb9HlKL#~BKVrm3;2l~P0zLVg}hQ(h;#C)D}K#HnFHkX{Df_qmX9U| z#4&9!8vdC{t%+5H&T07T*A3yHgNF?MZUJCgLzCvfI7LG!{r2cH&QP2C_nRAT&Rvf( zN+89iTw4oe$^1oAQggUb$lKN*AC#hu8Cx@s>I|Yjh;uI2dl++n$@plZ2G$Q7sMpNy z3$XI`9h<71KE2?6^Qp6*qaF|(4qb}!-r~px{lWC)*%fQc?G;jGa*wmF z4h=JD$T1Viq!Es^ghzK!NRkk~UG{^tB39x`lqtJO8Viku88HAdeLxy zpZ*Yg;O6f0+9hN2-z#eNR-uPB_iwtX^1%H8YwpL%Fc%gBcV@K$E*v{so@p6FX%8{MQM(ot#=W3@WL0Xa)QC!XU)QAZ!?d(=n#3B? zjlIR9RQDk2o~BRz)>!}ewy|lM%!u*mzioH0R$g``Wv&L&jbk&?N~bwddD5?S ze`rV?>9oV1x+W1=#nI%gB{(Pq^Dyrh0?Wg^jY^I7(| z>zw3*#S_x?r6Ep6p0`(e5!f@B8u9xfbp3`*Gs*5Pys8!Zx;-;l+1fR)4we*M`@QwTZYki(Se_NWFqTGqZg z%~kJsm%%adp>M0?q(vla{_REUggIU~w(I3t!de>m!Qp^#;zMWsV3W(SVv>wNX3^!_ zWkcI@zt8v+FQtZQ-ODI&8Gvj1bJj&GQ6L?(b6-80kL>_UKat~$^gWiW*Mx?U3+aZQ3?gqd$Z0N7{ zFv5M==%(s@-@1WlOGF>O>u2+NSj-abIe=Pw2g+XzZZy7=(f-oersRP@9c)d>M;3vs zGLvFb99Y|jS#nP3|0|fK5D}ttc{V%5le>G21nLYtZA#sMK9(uC6%Q32bo)4T7I%F~ zytyH1{4fO4wx9KMb1q4_DO)u?ApBJ2hv^9GXo*GMq=fESU*HSs8_`ytMe8_Z6-b1s z{g2P*snhEfb{HiVc}Nt6s|r2gPqc3nSd?+a#3{FfnPlyS?k_lG8bgdkjT&0TvB@t- zur7Md94uF|i7H_X-DB74e3@?OvF#dNV9}6=U$S<(n)7M=rlkTuLDeQP{Oy$wb*L0j z$!pR-nQl+6hsD3rS`VfdiLkApVe?)i{WF~%4~|seQdv=x#z3QAw4maipkA0t*OlE3 zSXfz+U3$nxoPYBmMveI!~2N+;lntnK|8(7ARB#{WlAYg4G)$?+yA1+hXZ4+ z+|yQ1Dg?vFT%(N4VdLHozqzU*Y^f#!4EQnTiD;ciq(G0Jec9OuLCmxRmpQIfRxpZBzEU06&IY+Y^V%r=kF;HDs z;>TZv&UWlbak2@K%?48p`xpj0x|I*Jhb4Sy6} ziqmIF>;s;heOv7jwctYtHM0(^1lk0k1fL)Sh)^*Lg|^xDz7pR73cy;X#=ZFtd)Tw1K;jsnX1(Lyo9kGB)F&Hu zeJUf#aya(_?EZf#+bf6Mc^l4M8kIJ;)o)n_0-ZInDTMxsLY%YZe6k#Q_DtKwp98ki+N))nnE@7~k z11A|EMVAOEK~(mk^UJBQSp1CW5g@7z7AOD|)(b#UJo~f>Ak}aQ)1pl71C+9|K*sv` zF6z}P9IS z+8ljAe`sf#*|k{{?U0Bo^2KtnJOs7DmlY>y!mc!}t%w&YM-YJ}UxRZFsI<9j^nO5r z0QUw$Q7QrnY|iB@_fpGd{IM?10ZO1y@{rF&*a1SD4N^{{#3DzLHEr@05CTH03|0<7 z(`7~YPWriPZLVMevi`>AD3(%fD0dTZ0iy(CG-)l#wKxl=4Yg+B@+HW?74jAiNwCxO z`5OLD$bIi!l+J4rz}7e;BdsIQs|B-AuHT}FrgEd}Um*Z_1*Dwt%E()xur;PyTdKtj zY@wY{_u2%STtf*kp3ZURuP9R?6tp^_Z)&WE1R4Ta-UTJ&bg93>DU|j34Vb}nX1z3Pw>#~KA z3#^SQwVY{GX#ujre&2MqG|IU5e*kDje>A^&IHen6HUPBe1`xDeAmt4puhx_8w+~r} zJ^xHX!+K?C8)awE?~4cTeVxXceC+mOtdG#CB%fA;L@H15bXgBK8}ZUJp!yK zkic;0`7bmq>jNbCK3{{t0SaLwXoS`E1q}E=0s;h`6=4B-Uk85z-9yD#9~zz$58%Mm z#3$5I9zzm}12513WUL5H1q9lPJ`r}u>a&D9`dMPtJa}MqtiB20u1S-!LV+ybW5c^Q zdbMRCtNB;(vld;a|F3Uzt;?2tyaGre-B$onlWmT1jUY z4Phx*f}*7<=H~wM>jXjcae1niV$#1k@Vh{wYv|K=S&t{}|LQmU_xJ#GqgO0N>rv%* z#0C35OR@{CwGJ+Dp+ba#Aj?w#i7n0VH-eoLi^~&Q`3QmxC_0t^5=b<{{{)Z4uyq}l z!u9z(u1V-D#vm_4_&Mp0vGfoi02e?4k9qRiz~Zq!gw^lO+W-(+o^RDU#qu|teZtO~ z0E+)P?5qi^hq5`|vpP<@2^|_i;jngSCPEfUQ7xKF-DVb;xO*q5KtHh?sXm6h0+B9XzCfhAM=2x|cj{VDP& zSOX|P;zPbz3fEbXwDhbo`3xXm>`P&4)CEb@D(VyWLb)j0}qx~flH5zRqpu)cd zfxyUQ)AXoOQ+Zhd0$Ys|nnKf~Ff#Fk%=OY(9-fd9WakL=DsbfI@I6{-9#{y!Yok!2 zO8ga;iUUuZ16A)f(IynlwbgO~LsLFolh6aXX8@Y4Ncj^~t2g=@U|P&GyFW@sP;hk5 zg1Vtv6o|H?SS3OolXky?UIsm_4giiRSym?cI`!o)Jj7&(#>rH1+?y>1^c~))fP0)rz!Xtv^R~}LJ))oNC+dL5g~}0Xf^I!wZE+vR5_;fp9O?%wtc>6JIshTE z)x;_mhEStsz{OHYK=Mt5e*wnx%D4+Y(0B%$Wz-IqOaB4@VbX7`Axol+9+>qZpsXL_ zdz3061q|N0hs-UXt)QNZ41Z!ESnq|%WK5dKFCgkUNG zCDu})hfHA(EfxqI{Lfy7>U)a`Vipu=FGtFkX?0B3_#_MRArLKi0tn5gk(shq@wop# ztv0yFx-W{Rb2!Qbz}i@Zm#`$bs11!xKqc)6r+uPd_WK_QT!U87jv^NZO~_URNj((d zWlfZTxX)S?NkPB^QtNX$);X+rHn>_UV8^y^&IcBrw>%6aHoPxwvofD#K|ThM{E?ia8hS0p)3TdcjE-Fp`A&m&e{@LYZ!Z8%W7F;x<^cmC zZKXvTy(NKqc|F0SMVjeI5VTo@tC#hZ3!&kCfTB#hW9-XXxfciD|3Jj5@QbtqXj;hI z|4K=E9Rn)@a0I&=FW))n%2^C3Ah0S1l|>;OzO#~?c>_(*2$$c0u6NT59|J&D&{>O| z)qE89@rBfFW zdtBcMt>~7Awoju`4T(gQLa96l8Kq%O2nPy7V2gCo?zC>}&!Ro7MaerI!{;G928O`# zNnB5Moi--Cy;rfiE%$6y6I2ff0mpX&4lF>GMJXdMuhPQYz*2sPvo9dvn!pJtz@wcl zrD{-n^RGUZh(sUJE*6`Bc?Q}N{o|YH3w@aIl`(E%jh2+nQ6;R7z2V-?gKTsxYDJg{ zd-Zo+FIq%a$Vt(~-ta{j17*P7OnRYL*Kv;LE^Po5{x3?E<&awvE##A_VxVYxqi*k? zZ$ITzIgo4Ziv7O7d-al+-Cqdy9!mpncy_53%kU)hyRYo}^-Jt80s%-mgm$pc0C2$N z0#FrHh_)tZgp$DUJqRnR!$%ROb&jZ2Yi9wB{W=c-bt($f>gb$k^hb27RUvy_ocsw3 zlApp#xi^~B(m*Tr-xz8crMBkmSu?arsQZxyyadQvX zHh!wGImgM6v80@;#jE7pRvY;!!ShtGh#wdJP|cL-dlsqgCac1ZXG`<6neP zfABvk843uM<#Uil&rziIlK@hAdS)$cu?jTrUegl98if$u@ue$7*3pIAHin{fYIEIj&p2bU~Q-@ zrfhs+>8y=5`tDeczA!Q?!Ut(VY~TShKnF~}^Me3}H87z0CfZ0q2%mNe2nb5hs4M`D z3~Yc1D6Z@HGRq0D<9*MuW+4Eiuy47Ie3E<^*RM8;bw>dSseU7D#?bxvPWc1`oQyce zK5chzpy0^+lu6H#2M_=zcP*mfUaoCw0VSbYo4>)z0*5|gNrm@~dGBrvB<*8afQ3cT z_IzhD_GUp=$UpQwOW~xo9FUfSq(c(=C*c>H283eN9n<~=$5J&Nq2Ye7_NuZG0t6&9 zeX|h@ixL>Z-dqaQ3f*EQnwshupyF`T-awY$!J_DR_tfz^m5`xPf&gXnSunH+jf$25 zlItYgRSUBSmGT3O{o2mABE-PrcVS!41w`7Uy&QW11pWu06m+O;h0wI>3LznAYO4T= zALRN#j)xOAnmw&q?x`$%5w7S|A!AmA&yg?#6$jdQ(ZZr{1eJvYf{Hr*hW2Qy{f=pc zOJm7e6uD|!Xg7c~5zRHazGrhCR)fC*7=#^lVmTq}a-5 z0C{>FQaOo0;~|>IVO!aHktP={tP2E$Il<35_-!(A z6S-=}A<($C_5g+Z6&*tZTYDJAa=%^=$6w*l+iEMzq@656udfq&ruon=eWR~Y3+*Eq z#!AQ+jbP!v=GXk1ZXiwt|M>%w zpM;P=cMMuslu&avkG6dH>P5XP_f#$yxq`S`uH z0gNr@UY}@_`+b202`HPe^Ia#G%Lp))ju7hjWp94Uk}Loc{<~dj)tjIUoKM#-Du!YK z3@)pv&qHXB8Y{vDB$A2*j{m?}Rz+0YIB0dy>uIqoISjB_jWN2#JaCH)G+0tIU2c)kba zNbqT4eFr48IH9F%`Ve52E&rh411ly1TK*x&Jl_MoDyrnUfCJcAD?oEU_huPejj|Ur zwLB~9;dv=kAK!hlCK9^Zjww)*+mzAPK3_7iBUkXKE=+i7gkuWqzgsVgzu&^k$tKl&S0tKLi zbU(^ydMj4%Jh{gZZj?!TO8~YQdZi?M6u*%fma^&1wmy=;frA2S(TiS7XZ#BB2N2ud zDxg1FjKf&4O4>xeJ{LgBSp*aV;3D}4MX?fof*u$?wsmZj%C>7)OGC>nFsfPV{+u-e z()?~3pB4@zXitmC0SO@Z(8MB3gt2$K{aKv^Oh3D4B2qi~1A_ zE^p$xt`oH)%mBuM0H6Xz&%xgj#%6Iu%7E9kd~~xdEp21r3jIC; zWXnGCJxa)2a1dIWUq=z;=9uEk39VLck;r>#zP3~&IasECj#a2R;> zL>Lu?0iOJhtO(&E!~`Bq%9*dW3QdaIphA2N{}WmQOBk!sLdA46u&`$C7wu|JJM#@_ z97KaA)6c}02&A=89)K~oqnVAKsVslbrY4N#qXdAXH*cYI04~DjJ_J)6X?TK0=m4O% z60=zPiXt)A=vDJ6&>F}0B3I|p>gbqpB(VCPz`74`^vVW;hSI4FXh|c#^%@7@@SSjL zTPxR8+qWeofY^%rs2t0rANQ3IEI^>3toroc%HF=EUjs;h5khZ?B~dM)Wtoh_yUk>; zg;?5Uf0d6K>w7gqfnou&RO#~oZ>?r1%Lur&^lnC`A`QDAQUC-s6Poq z3p)@#|LY6DL4i=NUPcLYs1`u@M3vA>76yPlt8g)p0v16)yBfombV8#%14W^)1Qi?u zJhj8Iy@yi!OWXnme?Wf`*xp^TE7j{a+`d;h_-#Lc({up-gmG-nM)?wox#<$dn$OYP z915UC_6p+yM$szcmt)Qc>pg}ookStFusP-C8pb)x1|;up`9~IG0g|84KAIz6py*Y8 zLxAc30J6XDG0~~E1`Hs4vM5=Pz_DAM>VL;(NnFcAvJ3_uB|(YGeAD(@;7IEN6#qe3 zn1JJ=I?rH;YVa3O2{ZxF2owHHs}g#kCM4t!>5W6=^+Z1O4k3PANKFN~kOBrtcSosBz z_#PDmc-lOH-M3=tdw}UZiWe9G20*er`Vz%7{^X5ZR@L2{B4h11=k(&zrZ;oOje%#S zn0z?l075~u*xtThow5TKzXKJ;$da%k?xFAB)tCj!{jPyE7C3a0ZJ(^i1;NMucV5?6 zY84>LlKMZ3p_2jyU{tkP4Gv)rv@{#b5J=D>g3A{n5U2%wJpfOinV}URm`GO zw4O>~pv+pbDy&D#odRh~0U7l2Iy;|78wkI4qooAb{q?Ullpxl}ECxkU*cR{tgzG2t z(WxhJIHnIPgPNf=C@8CxPn-3~KlPjYIR_y0BcWb!6saQg1yKTszG@xgwt$lrDM#X( zj9~10N?o?#r%7(I?mmMGxyMz=8~{}0B(?r-moZ?foE`E?dI$QCILwxT#M${r@d6P zV#lnAc5a^+Faa*Wpv8R%H6P;AriB01{tdZ2)ZSC}#IgQBqXyNy7sLFaSv_ zVG?kbV;yqG*w;eey#u|KDO*hwNc7W&ezo&mxrznKtM3}B<3e<00~aX!{}YI^8rLbm z;Bc=7N-RHMWI_A}82%@?KGpgs$U2zj7s;Vz#s7s&X?5J5M)x7mXjmGU_HQ{Kok-{j zqwp+lDu(aAEMI;Q5E)?f0u6Exg9BKsKoh2_p>kao4fQKK?%JNIV1_;iP);=GA4MSa zECm0?H3(VZPQp@EGD~RC(FQ=|oThaNn)?`QRFB|O4*JMP$XYbWG5XYV@e>F!%UlSz z7mEgl9CE^yMOkYRINGP}uAy&Pk`}_yr}eGzYhH(AT3TtXL?PP?kmwdFRl@kbM+Imo zDB`s1;qN1^-T2J4b3w(bI9In@A0kRG`aOkA{1QvtWd4A77 zDLia|?WZ+4)`Y+Z5C8yJDlI`(_z^TdnnD}X$^^R!QwG9d7K1MWqrLh_;xm8{ep*-f z6lD=6c3&1NK_xJ#91FqHxsU4;2&Ea??O5#qV%Go)fODP*6>8@g4kqBDUZ|M15U}f;iu`ny=xR zqqO?fcl(529ZOje;-XP3hW~TOZ#dWQ7a;rg->eFdwDwpac>~CXUY{&V!bbSkSLi!o zUjXd?zTe+FFVm?L@-xT6=W?)k(KIgJgge5UV2mHhn1)dMDzzx{O?mz z4p=IyC!j2Y&hDLpG${=)N)$&Ylmtr0vrYz;Q_EQgNcZi!gu7z9UciIm7-a1==q)GI zy2UyDEQou!2Fg}}rt=AawzVR>=nl&QJcVPm%h*O)1R{J)$<=NkaW7gOon$=;@#Z#s zzW@jzfUv))$dCgMNQ9Xm;TSXruLmH<(%v+4 zp+VZ)wcU>;@yrGvsAzuCF$2Yt0bq4tEQ)q}4wgwKxIBt`0Ta5`RN&^B2r%$HcIt7_ zEdhbD$L{GLIoA2W z_L&7K;qe3`U=mcai3A~5(rniMzygAme+I;slY23}{< zc0QYJ)$v743@a~sD(Us!S86*wIIE|&b0yS-Ijx}9Q{uN0!X|*faEwe=$p5= zS?~*%@*7L7qEu~x#XWPrfnlkX|8=UtE6$$t%mf2~|8+2MwDp@o-hDJ9p`y(<6buwi zeI2y0@$PsNaYjAk5)iUEmhx<`W#0Q!_Q$+Aeq0k&rpt@1pssU!vXDt!qwN=;Za zAHU@@`QLAVTft}x^zZwrtX1aDtf#63QRN%;wOIu2o^N^f0pB3FK}c%3fr0i+nbZ8biQ0f+#jqk)4) zVpaUV#c4Dshg_W#2cFgjh}ObVlSSCFCXQuM2xa++I#)ZCD6jz9NO)Kd^vb=PnkuB~ z7|)026GDO^AY#jciK?|`m;1A5wuBadD~<#df?wG@3Kl?I7f1lh@d1W*B^a)WHnMnu z!yFQcO#fHkrqA_n!rN=B1Is$-3lQH~)Q^Fp-+`$QYm$${F)w6``UH@5tg|9(K}rnn zd*cD5h*odn>iv%G4M@YOj~SQAFwjBufG? zz@mT*m)3!VKj$us5rwK?jwbg#%7r3uGCQx5XoEo85HckjtO;7xf^oKKYryCYy?`Qf zO<9}|E|17PC9@9 zO2V0NSO0VYlcT-i+Wr1?{N^*o3JPZzgHpP&`qrBxzIYDk6Vi@HJ5<^ z5kgh7N>G0Fum06PIACfd%C=a>sQYc~+| zMy~viwv?QEx*lO&-$Vc15O2=8zHEx~0>d@hPw>}r_`jSDf1}sX6}8H*h&nZ$dUF_i zOvfeoS%+R5(VJ|`iv$!_1$gX#XV3_!7=A^FIm1`O-#o<%f3)dtH=oh*2WSjUO#1_l+=ytA?<$dTe(jS@r^zMo2rAz|zXG?UB4Kqe z?dT;#zPIRI^b4SX1aNG99~NnudAz8t&nfPc>@SKLpj!zl%4Pbgaj0Oj)lUF`b!aW& zR#HJ3wcov2AHv~#%ea~{%R;$#vo{3_Osevmwt7a-ZXD{PwH8Ga*AY(<)!(+w^jm-| zI2IsP<*r3Y*f-h~ZED@KeTRG?*?dPp*|&2A2v%e*isyY-{ZdP{l;XEPM}3O}qg78- z%DzfHZq@D5wX8?hidAvnXjXHAWyE!m@LdTh+Z~Vq61CdI)$-e&irM6t%=-S8V`!g2 zX0Zsfjz^VP2+=N7sK1ob#erDI?0s?|g~v^T5(eN&%y)2a2$8E?R2*Sq^ln_re3kI^O=z4{0eU z|2roq{rVs3*?cIdwI~532mYPTU*CYRwsHu8eexT2(o#Og^)&*)I^<88@zzobATqlK z9YA%K2dnT+hXTTy_REDgl2VAV!D;S5uViju`rmPc#{a-o*qG%)yIjw}G^>v_p}A$> z31gwv7I|n1P&CcHaw{TQtOhWMV4+SxFOntXYCoX|sAe(}PTHK`l5YYi_Vp$$eJD@B zQUD|n=c@pn>sCQvfAv@`wx+LzmjS(yw`+)yHD`ie1zE3G- zS#rdiDjPrmvA6pY^bN~a7IH(iMYQrgEN7o*zuu6ic|xn&-Kgk?#v z9kV`3K(TMXFA#jMPHH1my|2ObTPvyTvvUZi2g+0j3Z0IA*uOK?vMSa0blx3+z|f!t zlER7xt^o#^?o((JKxrKoqmVEllxc6F2F|a2z{Jrnzal9KQ2YeKDj#CN`4EJ9rv_JP zEaK$5V_8#CC)(aDME%z*4>;C9gD$&AzLWcFM{9`LpA`fKew21q&1)-!?4GTX-+Ws&%~prm4bcR0}{Tjs#kkoBskBC>Y8_3%Zwk3hnZb2t7~$i*~sm`UD&; zM8N{~mMy>_fKBx!K$~Bv@DWJzBi5f-MYI-Vk7+?S*J|QZlb>?XQKp3Z)=h14FWQyW zSpTE*bH+VW09h;2OHl*R+k4V`XSc`&tI@pW>aKt%3-APxl6Y;OEJnV?f+JAaqE)Pi z`}i)Lhsw107upo>B^2@A<-BXwm-{Y28ng!Hn*Ow!LGnHDZNjREm~-uSObaX!{D1-o zqDq9DwIF;VSS9@kEdgdV{7(3r2-VtJt+NFzfdd7jRrxChUUr_cF(sBIsmRc^2|K|? zty(*n6|yhEAQbU=0?`|DHy4n0%{eYw)Z%L?o|MVS=vQ{K|)dGNT0O3s1CI*|ZDsA?i095f=Wo#@nFe!G51f^Tc!92elSF7fEv zQaflZ!cI$77POz5I7z05S>NpOJ2F%%eKw9S&NHNLd!21T;qt$G;H79f@BXF)dV zl%`+0RUV^C-JJBQbo)Ik;@LdA?X@C%uXn&fn~7}b)aEPLXOL;JB3Db7ihkj{G*}jd zlfOO(aCFQQtqLGOXW%`EfnRD>aw>lSP;`2ZUZLFnZ+)b+(o(BTd0H$h(lK4P?UqFZ zjKEcdrj_Gc;!=mR7%WO_SfM!TyZ8qD6kq|8R)`0bK9$w?S_gnB{L2TRTxd7G7)zq^ z0mZVckLv-5a{@yHPFPxLvaMs*QSJNY2C_H_n`5>(&~xjPEfd**NWqVr3|v5DNp5m5 zfYh#CCA0QipaAdnJ@(k%K(;i?m~BKWK)o}N98Lq*I!m^=sX^5d%?F7-ResL0usVJZ zAUXT_HASh)tpG&xyO(B}P}Bln1|D@GocR8=8Y~J6Qn;|VdU19Gl4c7efZ?;2W5KaP zf5Eenm<>l3aRF#ip58;!tTcm4uqh!?qgEfu7om($u2vxt32R|%00J0b@sSv`Pj1G2 zIL+vlD3;8-q@w4h)FciKc=RF-*_*i1z)K|vjsJ66myfRnaSUN=MUY-Cp)cyE`g#4p zRcH&8fB|F+n6l>qq_!OP^Y@BB1XIZZaztMR)IoUJ!|87 z5}35VanV2gMvl1zqI?YN!B1Ihk=6|$Y06gs+28k^C>J5(k5t}4L1aai*51Bb3t#^? zPFdmBwF)5a;aRR=-$Y*k3LuwK1&{!7o%B^66)U>NyIwU06adm%vLYNb9-uUR(_j#E z2|?f>p<1~UOO81Y;K;J4NZMu8}%R#TCwZ)wq%7p-Kak`m`|)_pjz z`bfX_jzS8J({F&&oH?b@kfkGzOVD!Amyfrvt!IgfIM*`^P!MLB@9lSebm{67Kz7+4 zeVWn{#qx~$Hh@(08QogiRU7qv&tKFl(AeKO>!jz0^lVEr-vGjj&{j>Tu}^}Cwky_; za?yl9(j1Mzu)aw-5nGnTF+h+L-U}4XngbpdM8^=c9Ph%Y`4+Ydl#+RyY$Cwh*%ura z1UKaSO7mTZG7X0gMyWQM5<&pC8kTBLHM< z9OqMAma$?zw5Kp_n(+eUL*Q7jBs{%hZk<}!B)~1203->y{*S_Lxk>aYg>wP279{0! z0kS}eTJ659_eQN!7#$ZtfWFTHB}MU$#h}SJj?6f7j{DjIO!Jak4MfNgRZ94yS3rVx ziGoS;$rLNo&>{XeiHM(3Vh})dBH_`=Ku`gpPVKzH)XcoffhtAGlIXV^O3yD@inadYRX|IzQaan#wEZi9 zWJLlg`u{1^d!t;N z)!kM5TA-}jpo-f9NB`^GI=)rq8xa5do@O^V0M#HRoPKw`vcU-oO4G5FmG2^rfzSCx zYzR%!s2e6-E3tx?@Le$M|9!8GtscUv8sEksXML*_)9*MPD}byeyC9EJ z1&*bh&MUlKUoSwqZvNRj9GD=>_5uL2F5&_xz26(%YK1^RaYGhC1f`V?(I_=birR#T zoda~T#ke&sS51N-2XxhG21sy1pt2T&rA>6(b5Q0Vfi!?cpwW!=d#0vEihD475 zuL(J#G8O9qq5uE~6lboWt|fM&asUM|3x;S`neGh{-dx0HXK%JP3z2DGfOK91*5b#2 zku#p9xMkA*-;REwR;$$=Yw$a6LzsbRqcaA02Xn1aLJ~k+JF2%K+iNYBR`flq*S#(U z+5gwlW&K+cv@r=FC47rEuB9&j(ej}wq%!IC!-97sQ48Q4^`(@nI8sU<@JNW&L|MlENelcR;|mLwz`6Ufe|1U1gquBYh*oW!hjLg2^>+E;^rlvcA(FzB# zxxS#W$$|hFfUqLw&r}JJ1t{?GMEmmXirzI&_1i|O)?wccI_C*6ZmIclFB57^3kB=s@l>*cM>yK>sx9DTz*+i+9 z@L7P|=LoQC>28|>%7^-VE#|%EyH+`8Eqo`Ws7>W19)O^51W}?QUHphv)BpgbDnOw# z%<*oq0MRkl#Fn)JD)g)Q-KZCf;(EZx%H$6TYXTVoMB4~(<{2Q;IC0)IR)XF(D{*Pq z9=Fy=T)!1Dt^>ZBDeG~0e*zkv>Hj@$dGnLiS?fdi7cdtbOS{%OtUps!=9Sh>NCF0M z_#`m<`|k5SfCrNDBj{4rJ-_cJV57IqCM>_yf`9;0=)7mR)Nh5l-wJ|kwmsUqeR0p- zXM3|C;^xXBsvPZQk>c`-iDDusg?ZCiwIoa6XB1Y5o>}w$CwYihxg444qrB z07f4crlo4+16UCElrx}#Shf~@E~L0xDkr^od{k+v)q}+=B;NzUV$SY$aQ_sLh3VC1Wj7{ zdqcj3=$CF8vr645=CvRR_FA(ie@S}+NQ!0sup7PF=j+-%(>uT@ENQ3m?B+4ECV)|) z906xRSQ|p$JN~LhCH#aOFjyf#s^CsU%yl|9z&Ng)OQB{I3-C}d&OGP17d2)%%4TO3 z?u%5)?DqT8tUyxCJ&;vijV4^pv)booYPV!#Y0LTtYvOMt>6PEI4)hM{74(j~KEjiw zxdPR-fFi3Q|MDS_T!Gq9_SR}O3sFBW&Dy%GEXzhDFF?u*C8ynQ0eorJQr)YJt_AA3 z+Xj69zk_xtb#p%yDFGY+kVw>Fm0S~aAC{qFy52hu5LpcSM8lfwqkOwEiMFokxQg+B zB7ddzXsdvAzIL!8Ed{gw#|nL>ePi-jiQNW_<~~=}u*G~uLRJ%%mA;Xt)$$2+araBF z0@9^N>mWV}_^XK0r+_2p_VR4w9w==4ENE63SCFzk{a%O^H6>TQZh1knmShV^DLvzu zlfDVrXjYnisbB)V&ij)uc*DdU-?gq%&=2njQ_DCOb2fNQkgw=1)6<(`TUfyVz~s}U z9>B0aIsqj1xrRpCc1|D(jJ;31zjjF8azZPVFyENef#%Y! z0P+M1`x=L=M)TmGXqGlyExY@A4TCo5qnve?fSgr=l5be9slvv5l9XJFHc;I8g!(PH zs*U?>>rds26k6Xa8 zBUl^&VU18NK8h`nup}a0K75iQU?N=Fgld&s3LpU@fZT2VOUbCTQ^jy`)htE12hX5$ z^GmWUH;-|-Z5DK+FCB9o#mkfH&*j@s`<7l@$9p3(*UIc~&3g|Vwih5PNK3H-S-`3M zh1Dc8*a=^xH#QY8*(73#M8D-1Hg;9?#%R`p1nLb(uVv{OqkREn7108G`=M*UYvN1q zj_##~twXenboD&|HETa~Ws4Lu z)0+bUf;#zrrR0F0FV)AFh^N z>z-g-0cmU&ENej)H0!%03lvWZxH_#Z;qg7?#xL=I?>KMldI2ZdD+9hvByGRISl_8J zU5mZ`UjRuii{|$KJHqZEDx=sJi1b@oWZT}g(460RJHe{VIDk}yS0h*pQ7leAfb?*^ zM6;+^mZK#;m8O(om+|MH=x`v}On5PNKw0aN#b};$;MnZ*ge0Y3j|? zCXnkG2BJG)S3|$z$d`w|0>Nt7?!UsFkC+AFFD@t=r?nD$@3lM~3;0$029oB{7hzng z6;J|6EEqE6s+s6E&#&-R>?FZ-72l>VYnBMIQbpfEa_7Fl1%whzJEJ@C1 z<=O!wVKVUBkw5_;fU;{o0gUQlen+bmwydX+aTA6-VBy;6RL(flTzU0o#+T3bUJ1_` zeOkdyVDm?!R?)_&;|l&d%xV83@T~T|;3^;!NXoAFvdt7*VA-O@tcbB)fRMB5-L^fg0&^{1?O(-l9s0m= z0YX+?_YELfg%$_Sa_mbGTl2K?9fmnLcxUEJ`m@ehoG2ZjsPk-?lvm$&7g1`Bzl-^cH7&FI3 zhnvbA&C2&#>$gt!wj&C971cd{qp{>vpS<}X0m4JD5;^NlBNfXo^LdD=v9%c=Eif#W#uI+fkn6%)pAWB*;0|TPIXlI9ZlWzY*~&Q zOJ4!MvGUExUdwT*&qi^w3XZt~mGCd!x`KG=%sRvCoL*o=D{4!ixYlcZLV^)M61dHU zbdLmTpC^zR-#%&Xs7(>A*3B(}Xl!;)`i(*+a|KhPSrpt8K&}s!CG6Q7ukADEIIcd( z7jFBmBD}REtx89ze0q(thWfu8K*_J=xNc6!ielKNc??2Uqt(ZOqz|9~iBc3LCyFAQ zJ;zB`b|N3*_f|FGhZH|gGXW03^ip|kUMKHHUIKU2Xk+M2G|C^@O!>V|{jkZtH1*Z$ zYxbW6??Zsefm|!|1j4=NlO=fK-3j>x$hL9ApqC-+`Q2aNaoBs02RZ5uyn$l#SSdRI ziN!RAEmQO{fb?w9uccNCl&)qklZ9%;pUrQ zBVZ*C9aG)|K;%A@ob)>vX((upm)_fKd%r@3U*Q5#Y$LxSDvi z{^l1F@~p=C6PxqMLMDIB_X=YeC2$c`UH9&%Kr_i*TP~t;hve$1X5pI&XQ9eeXh` z0D=Gz@5?GQWihAH4OopD@3=z0nKReY_-e7Xz6(%F6tW^Y>hd3|eS3|7t?O9&#sKq6 z6g7H=iWj$Yne^61DwLZ>Uq>?wa+7`eQ%k#|EYY-tajnM!;5yt-KFNkzpM<$2tEDf+S_gR(c56kNftcgo*!8l`)&lO0Q30f~s6cu_ zd1+s?GhieG8=RmZ0>y4WqgciGp**1?7;YY|ie79-cJsn|%efs_w5DsZ7S(QLK~Su| zvm(8VT-KZ=L90~u%A`Av;PXvP+Cstj7PxG6KRP!*HW#vUM(J=wvV-u z{N#_YBmmNzX@Ml#)nuvO4N@AS>JHaQ&K9H#VEZ@d@YLU)GYgu3ETYN{X#1|LaWmMW zM;hUx9(<{XeTz1osxcbu8n!iT!*5ZKUPacsF!Dt>>sb*+kkKt+Ue39>D7F%atO~H0 zFJje|C>BlA>WsY?J8O4Ma=^YhjHOoBqTDFZYVjP>%1PD|T+O@xuaI5Azo1I+uG3tU z=2he?3b*$yqPVmofw6DL@LGzsC;?-S4@`U9^+$R$Z9WFay-8BG`(~-7{7aRy6t}If zEKBm7<@^vpTJH5-j%m{wvl96hUF@Q>`69LrWI~uWT+QdfSSH7|VoFF=fS9A+cK|`R zSQF7Keh5mX{Jn*t?RPy{*c?9~DV*CgZ96WVNvN_6Pf4YK@)Rar^S$LG%h9jquMJo5 zE^wYGP5btlu7z2Pk*3{y`d)EqnzL%{rCS?9-AK>Uuj_y8Gv((jPzwDP6v`iAO?+Cy zwYeaHqc=sBUT~eBY5k2=BG#c5)jG@3({7o{IyUWttNC7Ib^1m- z0^f#84M5hz`5MHE00swqtw@urSP`=gIqEEjX}KtuXqG5e$;|3@ZX$50!qsX|00{^i zB3(Lm`)GeF*8U-3zW_-Ce(an}Z*FAimEW*}n$Rq@$cpTAgtY=|B}yhfY40azeL)t} zh>oL7r5Gr-n|I6i*wUunS(3~pqc;P-;Msm@e_`!b(O-%cKy2T2^3`Vf8FilHx=6oO zEk_&7#bkPlK6j&@pV zwKQhAeu5rl+0g6q`hH(te}%cu4vmPulNV>jl(xm>ahXvaWy6O^BaDnrlT| zZxzc5VEf%;qhB8a$U4&cqUwZF3Li>NFF+dG3y_%3%JcO@mOa#8xeLkTrXk({5)Vsb zWO|>Rm)>8o8OJ<+4_(EpYuQi88mKzbaemKoG#ioi@X2bFG%TWJrlKN6Q7Z+Dtz*l9 z=g7Mz?%JfMDs7iyEImonC;U;XEvMcHQ2g2mzrEHyn=hF z&yzJ;Fea!sVv~cOLJS})^w)V`0qj|xtj#Kc=%#*d_Ti=%&|{Wk-*p~9dM9BttFhhw ze4?+(_&V*KbLmyid4Z&NO@+0s)Aofeaj|~CzcoLdfVI4%hV(k(8f5@{_fQ&K7TuP= z(TC+|RmR@;jbZ@^E7AgXt)svOoCNQQE~T*c+E1W}RycmgdcQAYSXQwBSg@}qUNF4^Oo}64#CYY8 z1Qb*YK(>%JAEcaC0ipFPo&c~4WUXNP^QEKvIp^e_Y`@Dnro9PupG=K|9DF;pRZyIF z3lQt%e3mqgYxIsi=lSmXtO$!?E1Cr?O`hr<-%8y@tbAA!RI61WfkepHf)RuOIHF5S zmDb-_fxmf>>zCc!#DZWWS=UOeFkg$Znl|gb0`^3WURjo{qq-V=6M@USFDU%KzDT}C zV;8g7{n47G+};1SX(L8ab08t;z3^t=Qd?-ZJl#gJnq3$R*fy>;*>-uhWlI+zS&r6C zC|~yur6HJrl0UF{i4G*pfu`@ab!vd=I)1CubA<9S06;@>*s~NOR@&lsK=HkaQY{n2 zl89c(uq!>`w72$eB`!L>MBYZ8w)W}f4)(aGWL=@sOLuNK_YT%8Anf3+6N#S=i@EJ!(rw00I{^AuT;mhV~N*BJjtl?Zme%0>hBHDdW2MXWg5 zd<_nH^eU@Tb#L#|0FbN*3nJueA5iGT;_c;bZsz-zd2Dz$(A>mYAb6rM_FeEiX{uKO z7x%xu#1jaX@;vz--S=wm1UZ_!g0TR()@A_HKrFv~hqWLJ`ZRM9u(eVvjDaQ~W{IL& zTkdpg0heC&s)=T@=I36&ZhhztWv?Z<$-!iF+0(l;wry3lWwmVyeDzX=VWF*yyV$-i z3^+8ln*fgUvL=2nq}!r(=#@!OE!a>dU7Pv@)fm8c%S@%9jHrp_WuU(Ljbwgdh!Vt3{L>LTHZC+f<$}Q+Na^KPZ7YDMwL|D zEd1VcE!)-X{@2HW#P}^ZdI{+(fK+l)zbrU=#u(1}(Ce7@{IQ~3j4D3f^UhPCiPD&8@%VJoo9%R?Q*=lFg`B0?$zw$!Y;?$2V7_MG~sO z-KBP_t6Iyl)+9cAlZp$LR~F=zX1`YB0_4(?Yk8LA@ApfmR$Ja{tW{Voz53&Iia1u@!Si60*;E&88jsr()N zGI=V1@IhD+(JYn(?IPS&C%2B$uBf;3$SVoi+(seXEXTDZ*J3PSmiworS7R={*@RpI z^W>9U%Cj2s^6vV2EmEMm0$#^y*q$x zCOzxY%)*Ld3kEH9TyPYj+HDplMZb$07UF%&q_=b^4W2LphEI_tTfz?{1lk2ym38d% zPXbR4f1p@U0a2F2b)s0n${F|R9U|K0132;>B@9`SO5x>1oU4;}=2^+8Rzj>Gvl5g* za+87qB&RqKZQTFTkL!RY@D~jIzY(KL4OTe!{PiVPLkEOQbM`uYU%|*R&8JML5_I>= zLagw;YTveL+j7CX#}RJ*Z|scEPWuidn@lVxvI}S!A7gU?QYq7o&i1ayo_8%s)T&i> z-d+Pu0BPuG4q@|uQKkF2atWaCqd`L1Bq?n;Q;2Gs1cwE z&n|64rvg{jWT)Kbqz498gv9^~VDJHqqF4b1I7G8hE!UM`3JCiOvrP9cQV|Wi>*j8< zaFc>rl(ilUlB+>iIQwnQ{F9Kc=3R>Q$`XAHI4i7OW6LV~zU_MD_^$#8Fs)FNHCmut zP(1w>K$=gqGh6u|6`uk|S@vFm*DTQ%2Z;*q^AYm33j4hz_*$5LZ{HimYISwX?W~YT zZyW;<9QpPUe%}F#P)p>|`1v#~9U5)Rs+8j>jNH1sy|3N%T&oxEl=DERfMh`v-}&Ta-nTyLiC$ewv5B~)RZC-5=(i5)6&My|ufX+WvG&@l zS)(8qFg?rOuWPJjv(GgEY5}t6JzZn_AfJOS@U$=1K>_<;8c+#R?5;MgL4`c+?3QWqovWHaq8 zK?Nifn(?^+S;sqol&aVQ*{aC{$6A)RC>9U|hyu8%bBO?d*>j5ZCb2|Qt$ZY z8<^arK8ki%KSl6e)AdUVW>wI%sM@~8%JtmC$9Ess3M7Sl0mS)PgBA$Td2!lnL3S~` zj$7*!NOqC9=6h69QaQ+zFLIMp7a&&sG{jU!a_&2P3 zEoJw)?O&_4eX-Fc`!_7DKMU2DS>JIA6nE^ZoLEP_JX}^HAGjp--3Fj6$!GIHqF8|9 zQ>fJz5ZVS7TXj(K5P;yj4tOr7 z$Gp1c7NTzC>Ionl%3Yso{gMC^#fqXWSZ-Uk4Oy=Rd6{*6sjv6NMXTs8IQA{T0HV#V zy8y{TY=Zh@fcY#Bl7nvR6V=L5XFUkD>^03zD9d022*I}xC}?uO3A++pAN$oy^)ep9 z40NtvA~JB;NBE;s0m9a&SH9)L z_`Y*Zssb+Q*dRSw5c@9QUm@S`7bI8vKPA2PoCVS*%Hq~bBer7uT9GG*z5GwY*uAdR zcxv+kuC zyEeElumD}1Nx&0+`?3W09lJtc=zT!{9nLgEy!l$Qv?f;>+{^wkF{1T6ope?Q&9V^$lf}T z0&5JSShed$uh#!qinUSIo%-$kw`$(aRuk5$Yx4(K51&9lAX_h&1z|a|7SSGHXg#Ya zS+7)ct^6AIO5lrN1qz^aY!Nd1SsAp>&l`RK&cJ+6ZsdL#NKJ%0q0s1``-b_ z3Tf4|gpNQ)a{z*W!2&ct-2bh-E*IgqXqNBJzaijW@|nZFc@+Da_p$U!TZ(S^T~NWc z4+@Bu0kHeE5VmW3=)h4Tv6bxiB|+C}v@mycZQn9gt=FyqvukMWf(>Q4pB&W>0VEB3 z>CqLo{hzP~q6F|-kLBhI5W*Dw3dprNn$`b>xdZ0j+!IiqXk{QF@bznN4%o!+ zCVZO-x{msmI4ua)vTS8}017B?0J$1GXFWkJgu702Z?h$kfa6#~?0Zf#AHL)bXC)eh zzNe`wrVAKN>%Ffzcv?(h6MC=wlQiEes`N?|C!p7YtiRI~gyrN<^+yYm?zhhOT9#J= zzxzK%_nw}$dsL?dBu@b8U6J{Sj%$V?%GR?~!O?0u`54!VJVm%Zqgc_FGU*$=qMeH< zu@pZ3CycgPiV6t{K7l2mEQ;TAuorla^Zz>CB?AF7;O#lu0vLeStEU~?3XDJt%$V<5tGrj_IwegcLvEW#W z^mOc6jAH(~kl*?x7jzpfOG`V(`4?0hHojTy+unV~+OKc2QchXo%Tc&DusSy1#Q7yAoukcw76og*IzM6Oh_qY1Tr6l)yTs5j_z>mtI}=a69Gnz|H5mAYE`Rc=Ejh zV%5H~?5J%0ev42y(%4$kn-|f}1<7XC*J_misD1AMp*a})cG;?HH(5wXSpz~(@G2mz zy0&mIa5#1&Tv;8FEx%_y%EWJImvHYK^rqMb5`gIbTg+GqN>#ssOZ3b=MeGP~+3`xO zb!ef&tVY1_e^Y~dhlzsCtdD`Ij;IB)cO{iLaWVb{$j+&_QJ)JA|0j$ab$W8TpQ2f} z?x_Eprf`Mi=2_OS=(oN1UVo`v&$S@E&(ZM#WkIqNs!~E5%_`%*QOVwT22=o+wd+}T zTKS!xQJH(R@vT@ffK(?XvXuqd?7B>Ng3gK%x-ADGxC;(o$bz(NC~Ja#K!7NIL+ ztQ=)$ChkP5r-@EmLQ?YWL)PO8)D`|GfLsAxfLv|*RFkylKMDPZEW|5tTu?m$B*jum z{6-M>mF3Z_z)`7?u`M^1lDYKisqEx43$m|U-s!Ged$sd42rR)DF|sAFwuIi+2Nfg0 z{m)k+pw3Z#0wk!?79Ez;Am{7t;YrJt9HGzWP5(ko{tttnpl-WT1ygGY}Z%Gs!z}f~FDH5|Bl#)|&X9u;;tj z*Hl};skY1S0O9|D5%}^&ifREZ3!o!2!8*|(0Dw-ZDm0)NuqKH$C&qDsEa$w{880|? z$;(^dxY4YwgncSY`CHxA%HsLQvvH&Sx;#~V}^G&isyLHXLtqm-I4lHM(!%!&O)#mZn7P89^ zact!#0i*>XngY7iYAb*X|K7m*Pq5KDF(Z zeLRJi*E;p=^~sjJREm=0zOU|hnpVB5?r*kzGwy-*T9h{n(q|_-<%8@5s>)IbCz?bc zn>#`nqftPC$^;7EdrMS)1Zq|?Fee;ED!EuhsyQDEp6apyr@84nSG^jp!pG%P0KFwk z34A_;j`D2w-Mj>irqle^iZ59aom~{mAT+08*8^UV1e~Hy> z>F4dstm+#;wxZYnRn^kotx?sY(&bACc$x4PRcKM^9Qo+iZmZ#HLR87Zocprl%}X!W z0Sp`3&H5Bx4sh#}M7vz0uyZfrDQT#~43Otq=e#hd!E9=9ud0?C0T!LxY_=xtRNcEX z5UbP5VZ8Z~o%eBr15eKS$29cEtjp4?gm;BLnzJC-GK{CyY+Cl|mXzOwDWNhVjWGTUf-}pZsQZ+ZWZun0Kvzrhl)b68Uz53o{RMv$v4j9obl+1pDU9_sXA*@EgDSYdNp+K^BJf#cJ)J958wzOy`q6o6r1n0VC6t|MaP{x(!gd}Zb9H$k3x;>dn~8F z->(LLvIwt)anJ3(tNBwBCD{_7mToE(9#vbbvK;?IR_3-%J62&V&C+kJHnV*l7#f$S z^#)qrsVNFs_Qt|Js}lIE7ON?>(X7hK2~Vq)6xrgmH(w-+)BiL*fz65(?aGSe7esra zSItO_9t9rT=DNEt%d%@@0Z4N~@@q;QI*!FCP{3-n>Ixw@SgopJ>;EmA@Vyd+0MZIC zt=6KGg&3cfIxTLWuhKG^uS!pDX8qEu4~1&4?{NQra*P*1>sxFdqujxh)!4pim6`=m z=j7+K4#Bl57Z^99wPh{~lBHP-l2uw;CwJcfc31AtvggQCl5dH^n?bMSJ)dJ&J@K6r zzY99#+yjsgA+C6ka5hzz;8tj8oAbRii3KTwRpiNb=eif666!5Lupj}pLc}ac_i+ut zsC}71^tL4v?>e%DkMGhxIgK~7-2Y2Y68N_EL}ZqK11!M!E+;%IL126~tKI~sz*LF+E*Z2TV&KV=(AoqZXqt>=yZaJg;Al2s z(Wxv+V9BDmUdh7d!i!D`29$phW=88giveA_*s?_}X9NHVok2=SDt%56HW%W0j@yOl zDwA(prnhqWmdbpZ@4W!I^;i|&T?c)w$`il8IggIn{jZMM>v?|9+Y+@Y&sN$5fE0*r z%RDUrH=O)r@!D6J%u^}+PORGMg!?*+t-HJLn7psI$kC4`XIX&fZPA9c7|k%WZK#;U$+C-m%0#7a?iMZ|J0OD9$s;>Az?$Xb&N zlBcBLwIXX>7C=R@^i6>9y_%-vBkHuljC_;plWbV{3DEXc36PT5KG_ib0%RBZ%PPED z$v>*wl1>Ft0?yXZy+yHFX0%9EG|K-vB-s=%?!lkT}W@~le!Lp5K~Bij;$%@PEb z>ba`1s%kf<8%Qc|ZZNFxGFKFA74j%ONi{g9ql|Jk3$ z%9O`_8zi9UJ>jjwQQ~S7glkzg!m=eOYc=}*O4t?*tIhBC3zGdk3%pw2ef7`nyUx3_ z^KKL@nswW~3g80dDvyn9-JH|D*l6ijV7MU4Y6P0D_vVAp`1ubBcXX-=RSSyfSNqoN z+E?Zs=l4B;u^^R&B>1hRRR910TBIS6bWT;nfv6>c_#8#GM8k?)p%=Da$g27BZEcj4zs;x9n1^S&Ve5O7}oc4<*L zB0+=BiH5emS(<;Z8Kv4e-&+TD+sR_B4+0vKSplSl z7^(^`OJ42-V1S`8ZWhM>fvRw^ZM9)&R|`HE?A8p9LuWxMR_qmY`5z0AUKZ5)#3_$n zE0>nH-317)Q@$3Xm&njyB@Lfu7Pjx^DK@y!_opb;(wdDNCHM;-&t4S)E8#Z++_nnm zd@}pXBb8*$$}D)gZ&AbAbS=mF8e5XH%Hk@L8$OSsZh>g8w8#$$P*3@&w1eJ}H3u&5fKFe0;R+^I42n;0@T{BD5Z&{SKC>O`w z{c<>~5D4&rByHK$%=KL=HQ9Hi?ApWYkS0hs67w{`yB<`9k_)uvN9?4jYccl!CyjkI z`_=F{mao@JJtJ)XQxmKiCLIkb=vzqzx?42le zOWto*y>r(}Tz_E&yzd`!&Nr&marR{`D3C(n3!?n9Ufb>218D%c_Xmz5g-@2lZ#%X3 z(y2QQ;pUUF7#rMJYI`lp_4{(>n;!&B@3t(wTth-lGgoq+Qyu-v0WX2qij0K3`RuL8 zNSIsPjW%ylg%*mdFtOQ-QM;0WyNTWW5IiF_ktuWtm?Wg~rKys6JOUnp*RR(GJfLR88t*br& znnJXW$tNul?f*dHxLpqDMy#TmPxLD4`UH?HgWqm+&~HG%zvz7;j*E8P{K%V=PT*VG zCu^~_Qt|xejhENhRZo1+y0jpiEkUo!mVgSy(&B`+l}?(kktOT+YN{@kD#BG#vEvpz zS-+@Pt3U#$?+qF(pNpO78b-JuYCb&+rueTd?@P zK-hUXJ8`aJ#*&L~0NLrSn{UW}*p<~6AT4OU0NK|ltVN0vHBEP?&t@rrsAYwsdI6*f zQac|#OV^S_0lRD0ZlD10=Eys4tw#dAbyh{W791BKpK?s+>^94@R^}COb_I^*?E#>C z!xjtP()6`bPodZAI{|`o%98uPYTxo7t5B+L_FRO&h5LX%tK=G46xRZPe5>`-mSRPL zmI}Qp)7d4Kwo*5XQS!E`>*}=%$(K!UNvMh(-|f7M0I@Ut?5mW#QuM4xzDEFQS)X0D zC+d^tPvZxeUE{ZDw5`$@7y?i$nsluIk)s|U`oGX`Tfe(De+1AA(?$U{pA*em4Y+{l z)eYCutnhbCz+39paht3RFbUkg`mSWI@L9^TG3g6xs#!8&Lb@0Dz@E`g`gk(}%;dCzazl~0-owZdJ(FCS&W((4jfhjsLe zW<~u1Z56)Gt3;*uinPeKbDGhJZe>Z*?i-Q1TE4{KwFEir3$A>T1;K))bE0R}gWYQd zlnoYakzl~sh#LV|fNYX3a0QA@e)c_q)+XPJoNdmc^NVoZ$^`#k--?iCSzOz5$-HqGnxF z*?oDJmM`Ly5W;+h1xJHlX~_z8K)8aOrEtxJccWw*F5c*v?}RUnyo>rRCG7X>^l$X+ zRsjT#s*=}l*!R(w`>Bndu|+%TH-g3L7PTvMUVWPXH#)YU+x{ywyE%znGO6EpInjKL zW*V|GWzNeZ89$1f{AxQ|7^QM-tJjbl3fHlf3$=C{0J`s6n765^Sr8Urg?YSt|k0dZf)0&P$utpIFVGBVS^(;6iOon6m=6c5Feh)}!x* zY6G0>N3AKHC~8WoefrjT7WAlnMYGDpZ$Ioa(DDn2oh4YET7ueAqStvZ-%+#+a4A*F z8!$NAEh^{zFkXFOu?_cif6qz|$%|(p8!QOg<=XF>7akx=;-xKm2gg#Q)zHzIqEIck za2Ih4bh{8;%Ze^QN>)akDa0Hy{x|*~UXtGp+Bx_QAR@c|*0c77#NWgUfVRetz;3{;iV+cy;vJg4`P5<@q z(Fg-an6z7&ymsUdv~JAkJ^F2?mo6)WF+{j_ZH*q94zpKU}xD*9NNie3h&A z^}P~~1TW3JjFTOi~b}p+MvCD2S6`E>ebPvC49H<_TAnriTy2L zE^4K%o+)OdeQ%{EtjJO%Kyf1iXpncBtnYmi{47BGo8*))Q=X;VM@5R4+CBLr7SPTDG}&=-#w>3^=$cH z+Nl44#B+T2@BZE2fZ;oE_--HSRW!@-o+}KzOUv#Y@WQnv+;GkoQCKQQP%Aj>duvqr ze-~cxe=CkeF=4kU!IHhZwNKt+!P)-eWtwQar#py=RMqj!FZ ztP1eT{BDAafEpZI*Kf7R7T(KO*g7xYTld&~HnQfsHW8L)Af{mpWo}fbT*9tIxb<2< zn_qLo)f+h@yxQLi{IoGaq}9D_h!B0(@A_Tez7wp3iG~JF*QS`_@&UetVu6nQJloa= zpb%Wgu{VmS|BM%qROwvE*Z;;Mflh`3uIKihCDNCum1p>YKkx^>|G_`_2fzRR-~ao6 z_FP}`C13LXyL^}L@&+93iGq`2o7MmcG|j1;e3JqPqUr;k<|QJ6DY5{L+11%x@w+N8Z3 zA)`2A5hW%)7a-8G-|KsQuQy@xK~HF00>!d8hLUhyeXb9TkLwlA4H$*71Bm~NDh1#a z5^#5%HtBDGjdGOl18Rw1T^0>CVf^ zu1;weWe7;Xsjph&81M;&!R;-sU9)9tu5;TgxpE0@^I@u%we`F}(B}H0 zNK1pWi7eji8CJD1L7covSVSv=QzKBzp!~D0jpeFmfLt#-r|_*v3iZ0XchqESFw;ll6CaI-;CE=6w3vfI;gJ% zBL~Gk!bm`OwUMGuMY$5pwI=-dQn%^4?wjSq_3P)xUstiR}^bt@J&Y~hZSjW@0Ot}x?${}o4q3aSZ z|I>KJ12Bq$5#TIO!qJu>ry=u&s*1hyMV4Y!_Msp2X+YfdaLY;bM&M{w8h0s|X9E_( zrw`lnZf2(UYz7$38nOQk3f|@+ISE1|$Nc7m4p{4}B_e>g}0ubn8 zR>bqMB+;$6cN7u~e6Qtrs=O_Oo>kGH-Y!LW3}TtcW(#g|aD5TlWF77RRHi%N)L9Fh ze3CSE|2wycSao4rESV*#c5E$)?;HJ!AG0KdfwkGRU0|iji_PQq1OWwdKmFyr7Eb06 z#(4=6&FhoqH82&$Nk}=i7x55m*V6VD2X8{!=8fvVsuWTvh1&D=wlpWJp5Ri5uG4&v zC|JNN{MvgN(J5{9N%K+wJs&}fo&cLZ1@Ow5w*4h=i-4}e?3pSFdCHgdEWlAT&2t3) zXl(Ub6)m=2*}n4-7y!fgEI>TZ8$f7LAi)=Vr+HR`!0%Mp!oMN9=n#R+NjA8&x`z(! zIpG1IfeS=CpoKsK$pXZ0X&Aq^`g;KB<(`F*bI>>Uvc;AyvAX3YCA{>D`&lJL`_syW ziCG~1L}(b1nYsOJQLG;9Tzt5x&ciU3|<>{7AK z!!dTixFPGT74Une7Q*YLL%lfDGqNiABS1n6+wU0X5mJ`9fJv{~DB0RFQf)gfzvEV1 z_ll0jx=2`2vcLf-&7tuezBB<`$py5qosT*kuQOmfCSzIK5&$P-00P{EKT>dbsS-@ z!*eDHk3n_6>b7`kJlF9oj$;d<1`-2b9aJwjT4CCS z{E8H9eb`gJqtG^xm+>xgwH3D2I92@DiYzUs%BpC$=O~}B+ShN6ZQ@mn4DM<<+SgK2 zP0?;~!B*SZ3V~gyFu%t0Mvt@p6-qWYB}!T{x99Y1EP08oxC&8e_0*&JccwyX7{bWYhO5{|nb)oIDT!EpCb9FyyL>jv_cnk5i5=#JGYjORK ztp(H8ax%_YAuNYsARGfA`DjJy3~Ek%FL3EiN0l^G;*eHKGZ_q2t2gWhkU#|_g`Qh^ zX8ols|4z=@g*9q_0=Aj-tpd(k7S-A%bt);p!2#5(b-s!W)o#zU&xZTEru$UamZc~c z)BcuzTFbIIrS@lmdiJh!!BW9w+d}JwfwhaJGiJsMP*PSbp=Wsm2%%{XMsp^rj4dx< zkTjs&NKvi$pioQLRG0S%q7=QXSb^jrBvKfJNTo zx{hfP?gp!B)*?%etr)K`Vjaz)O>p)lpE-sGK&KO?1|WL1+XlIdA=5~SG|yOYb-p=+uFQU%f|1Uc}HUsVvVSt z#xb<1{mw1K>i>;0IX9=8EYyVdUVURfegt1cmgr>|qA*jTB z`Fj4;`c+#$wgrO;RE~O1ZAF2%Cae;Y8+s;WtjXpWa(a7JU9$?aZmsH>;8z@)h1mHq zoB7}GO)a?numzidQ7&c`LLqd|*vpSZ6?ZJ&N=*oFb1Ddd2o*tOHOfwwP{aFCFV-RF zz1&9OU)Rj?07sR#y~{9=KRJHRieW24J7#_3G}`rv+wzW;e@1>Nup8B8Ofn7 zM41m0g(Lg~p72Mx3JSE}|9u>nH7e=2rSs9He4zb55M(KevXyner7?w0?ax`s*-pChE*vl)VA3C?nhCoc}l383v%n1(x^pt@-f!$SbCP< zQlhjhc5_;Krm6?p0#Q}P0f$m3Y#qfaJI(hZc%I$y?x*efBP|tGRJ7+y?yej*ccu1L zr1|8VWs&kxw)|&H^tH=avHJOr?+8Lsr-V@>YfwBk2a&K>M?OO(EG$Jr zO$b}vqzO?a5%YUku$=TFRy%2`$WwGGpJStFTPf^&;8;4CWva5>$SnkoN+JQqa!)k^HP8c0CIqX-odyR%WD_@=van*k@cHBL{pZWNn~j zCv|02cV3M4R7s$Hl#=IoN61)@_%)$%VmHcE7dt1+B?*heX9);m5s7`R0WFcglBFuc zzQV~b0g@_q*ICbDZ=I_ufdj%i>{*EDl>fIVvYN3h(k3*U`MC94*Pn5{sMeO3_kD$H z7dzOAKPBM|L{4rIl=3No#6Xp$c0Ga>e>YAmDEUNp&PNvEMs`Yy?&1zRU&i-pqzaps zmA}gptp?ukHLD0LMe6W;$GUcFH*d5tD{?1(T|jJUd_HotEVkn<@vPf`9uw5M=u<~lxH#KQjK`B9+it+F2FO7cs9qcHP2 z=DWg58b6;SZNKwPE;!sbDz)I;a(w`bX5G+nz;P~+c-A5`I=QfUgIP?z;<;U^1K%wH z7Z}W6B+O2k#( zQJ5Q8sv0PfNIn3hKuT7m0K&Cy3*fbQH#)bz*3zt{r#bvlwzBa>xZV-WR`?+3m3}l! ztY{N0nrSZ?6%cIKf$aCqbo#w{8=H%`$wL5f&&pl$iME)s68N1`TP`HWJ&;_0REkj^ zW@kn=O*lH(wJw$OEI?VQM%t&jCIvL22mKx$DG3)qM79D#)?)K0_VqFz+LsW<-}QCC z2nY#mr6c(ZEk;eSn=i4#-Tt=gzbqZe8YQR!bZMApL{&t$w11Otz)w53nu&g+Ko=yf z&{@g+wUT%KexTNt`v1vsk9OT)MvlL8>ql1Z-8#)>$g6Q8(B%Vc14!Yd8)->(XJ)g!nHE&Ee%B2%Z#rUDg>Rck^hku$pS_DHuStSZR@>&CTpmj ztVpFM25|*xX-)%3NC^+&=zSDTH!az?lakOcpy<`cG;*)5j>GHQ1~xEeu>!}Ilcf2Z z1g1SJ+*SH^PS3RtbI+F5Ayf;@08(}H=7M~0Eh_!syrOiy0;8N1WmH(Z$YJfL$PJ3h~VX+)!(j zYe7=*AjiF^SD>j@DWMJ|8-6Y+xnOt#NTJ@&ZKh>85^b({(EX~q)ekg#nk}Kq>?gPe zHI5rddOh1NFhGa^CU68I$N1#5mn_u38>-EEtW&*Ha|`1ZNa(nta7C+{hn`;=$KUYr zlSAKZOHnJ&uvW5X1NyAU=1BB!KBezKQsOU1y7sluh3D;=w(7a^l6B&X{B7PPdCigp z{5K0iXjgz~$PK^se>7^doC}DXpt}DT``-GSTfbFRYO5qxf3?ZN3z#j(-{@5(Av?u4 z8dZ>Bg9Q~#E)AD?Dtmhcxc4C|_ww{zJKq&hfu%YWr&9Re%ZAb>>SZ0!i%-D7N;s z^k;qNTI4Glp2aR0k!94bo@2}ZEBCkEXDcM#cwD!jL~DV8K>tny;8>Me3PB{RBolp)SX4R;3x!~e?q{whMyDExs6eX;-cW1NpH>GXKovXM7S+n{@PERzuXzJ3AanhqH8gE=S`wDk zwh5JeTSk)QAXLqOY=VF|V9LjVXIh27fUI#FAee1Wl<+`Vaz z>y`+(PO(bp+2{d~*4bJ*J-KSlhSgG1-XA`z<=Ok-I8@Qvxic z=k&}#}w2T3hc`;?TKOIqxbWMU(b50-LqYVQ&t2)}#!3e#pMxf-tQWNoWa8{JrRp zevRfuv9bm`_hSLE+M5fi zKmys8qpZ(TlF~kjL)WSk&$S8d5c(z`#wdl zvZ(8)tUuN7`BK_+x+`n?q~W|N$9*lw^+A?iZR0~;0LfHsg|7$Bf$w_)vyNZRpLWFe%TqMF zu%gC%kwB6~2_%JzH++1Pg4dc9HESV>{FSb;F-nv_NeXZuRHwgkjQ(Y3Z0AR7=^3v!c@1rY*? z-&qj>hJ26cMZ#8AdPAjY#!BX&qCic$+HExJUc1Rv&BduAyJEb;&V`B7s5$ftjDWOS z_v!qcUiU8gw0X0#;rR~kQ8KUft2V#mew0Ru!>T-*P*suUZF6nE;11xo8aM`1^3(M{ z14wIHtuIojbrXkcMXsN+q2ZkUP315BDza5+LeYwVbE6h%!JQ(T1Krfz7L)50ZGmK$ z4SUN^V0(XQ6^CFBIaHm9)g;ug@QwX(P=iUh#9)fZ(| zDiz78T}{6JK!FYv(0J zn9EjfI=^gs#}xq!#YJRj*!yqm0z@! zE#T_p3JN`Q{JwIHtt8m+Vtr7lcig?~_h!L2A$DW?Rb% ze=SddDH>VgvXsF3ri5_)63=nzUjknd<0|vQ$yF!poRPq>l(1-9%3+_Ue!CI5&j8Xq zjip>0;kr)xwKkV-Wihq{;iJTsa9*}k>^08R{;d2%8CHVwIqZ+OS0>1 z-6Ukc<>LgBR+`V@kMgY*D#F%8wEQgs5kR6MIklUhx>PhRyBXoFKPv1Rog*lPN290# zgn}`4MS-#e3yuqrEtm2A0&1f{YZ*4Dby-FJL{+*efTeqDz1E5Z63>AFWzNPS45g@si#BnS5>u0Q0Y3cp8yTDyedvixN17D8BF`RV* znIdQ)62>A~H#o7;KfmRR+{9S*QwxxMx&=r@pw*tSD1>b%KHZX_B5K>_{HCpjSZlcZ zw;d_Ct9*^CzPVa`t=3v7$L5Q82GVL=SpSLL1em>QbJcWX9 z!m%jEwJuA$GQ)-LHX9pQx=tNajaJ;hh33kC6!OiAEJ#|iC`uOPTIYOyk*r4Bt@XMf z*&Im`Emgj*7f-%n?SwK5v;JHCw2p1z-_3Gw=D0ehqGS00DVHXU=|e(W#Ow-o8u`g8 z++zd#J$55>3y>6V5vi2N0y0ax{!bKXm1V2vyn2oWeTnKKRa-asRQd4+kSbDJcI5)* zV@~`NxQcdpkj=PPA-vRUtxN)u_DopvZ*tb73*|&AISClO!w^6SsLz5VOVhGN*B4pm z{DzTpzIVOgt?#-^2(e`4L^9ifqyei&EvunE5u4|#N_#W=RgpK>Xcb4`%s0q!&I;sL?JAkoob9u$HCQTJzpT97 zTC5_lZ|h>+RXcVyj|-rk2eIo#y~?MoqrWsPU@VwcFqV#0UZ3W0uU-OE0b}cFWkqaz zH#(X_NCU}6wknZdCp|wTaNIfYTkn;_ze@>~NUUvM!lS(gujkq*Rl>P}iGBWJ{?0G@ z^pwae0+fI+;L2%jki&QVA19CQ1=y-wZ^3DT_e4(?^c$f|S+;V?0t84l+n$Bma-6J@ zK1xyUikW1BVlB{9+w1422(bs}pD-uXHI<<~^;Mf)COFAw5$>KV z^S+hO4Qhk4Rn=+OO^iKd!3&F~ZSzA4_pV~$j}X`__R@@kCR+jVs^WD4(|NVGfJ;;_ zrn1Vl()_iofh4O`-)N8H16ot5d&kSi+Up)!kgJemEbZ54?{-|&{{Zr;Y<^1!He9^EOA)iobno_P9WR2qA>JZLS&>V%fT+3bH*(egIsZF@@%k_u z_FYH4dG!X+`9MNgdZsIsY3T-Vv(Q^w5l_EPY$4J1M>Vn{Tal1r(RappX-@on10*-E zwE>o%qvY;B*Mg;JWRvq+N~r}1V+lp>mLBc3wWX9qp=%a<*Hhj=&nr4rj_sC7E#)V3 z?{-<61xQQi-1VW>!t7EwS2HXX%a3^q^R@4Sp@z3Vvw8uNAa3b9Z~#V5I&h#_wl|`6 zCs<`sigE>xrEZ&@Uw~AqQe>-%R<>W~>wKNJc`e261He*%4R1dU;PUjFw7dCHXms`@uPm`b%1U-k`a zZ!9tw7{MKMfr>SOFEqD|CrI13Kj@K5uGOx%osZ*|x3F>8`Ne+hM-m!sz2RY;Ulr~H zo4u`AVJSc2LuaycB`4WjogvJUsB-MY0QeoX2@`WPLsBk>o-tan+x6d0w3ce{>)+mh zQLmuz54hpXD958Z8`XCAryrq0RSt4Xpau_B?;M}AIrZDR)WuH2`}=a8SCgciz;Oxg zZeZq;J;1Eb;!FVRJj_!vspIPoI7@mLH0HJ;oM_9EDdBVJOB>`@1^ob`mes0y-mkMjhEs+D9y?dR{E5%FIOn#F_Q1*kc<|b>$M(csU zf^}mWGh;07X|s|Wa?Y7Pu>K^F(R^m8A(X&=@%2-7Z4Df1e&bn$lTyDsk1qKEDW}2B z@x^xm1CIXgr>aGDsQ-B{QiI>;$RB;TCLG<)D%o5Mym|oOK6&u|I#RO;w{4=?ZgsOv zT!vQn+3r=Pp>_7wt2$7$(Z(ns? z;}iUr9iNIkPeHZ`iC>P#NzY!ur)-)mO|~6@u!;DO8_VC_HklWjIq0U0)&J0W&ylO_ z-C#{a%h~VdV~p-G>Io<|0DXvMBJy>cRC!e&@$ zusE|>FMWGOt;<%G9&wqo4cU}0zZ3;B_xxE+;R_M-%H^7z>>mHf5@g^wI=|WEOhjA$ z`kPyhZ25LhMZCqo#B*OybiXMk6!2$jf^4tpp@ar{;wD^_7USnH{#0{NYOH(TsWAFn zZ>^C6S*~+=h@se(jmS|cur zpz?L?iU_iGuRH5f>XSVkdYq?noR+hrW@B^`A-!i6^w+ z9R)wVFC0`9C?)oYe4rIfiWevkuy&`~Z7t+;-2$9_PdJhpn|W(=BWTwt&b|B&W4$QM z={If+O7vMI(NDu>yL}~tOg~p7LxjiC?tQYqzjLVBev>~(7F>!Ho+;lg+!dVmA=_6+ zlt*^KzwDZT(T9+BiKZli-7n)aOo{o1wyHRf8(;A`Qp-ffh!N0jv^}`R@GpdxJXm+;$&DkkmZa{DcB#7DO5JyHE{pJL8;w3~s}LZ1&S|BG8ng2R&h_pXrSl zvm5e%YD|25j6Sp|Y-Cq=7Wjp0n^!kSf~;8gh^i$A!iF^xr7M$$8El~R)(PmVU5hGC zoX==Ir$%~~cRrl{C>-~TeR$5U80b`5|B5J>hq{|Y{0}Q0)4K9XTje!F`rMAC*ZqQlV-P}uitpFgu@X05;sW!)vA;e zi51eG-XbeIpL33He83a`Kb@M)W$xCgb*T&2U%}JOV`=v(;aqOWB>1k}u~@>TbG7qZ zx|_Z&HY3*wg?r2hBmiu-O|+hVsYkW{U(Wx1hgbdUw=rlLm+PdQ(Zo;p-Wkp*e##0 zmssq);o`X0wKZWa$i|`v*;>yW7w z)JNEEFyKS^(Vq)kDSygMdfYz+OVPu8Kf05=M@HBt-9=`=3c1^lAAJq2As!1eo3p*2 zdYgzZ>eK%LD(zeK4W9jk26@{RFAb&^c`@H|d7f-eQwB64XzSfd+`QAxr!@_~^OU{d z@z_;M{iYpzXDvWdL$*q%@!rsZ7DRp??zI){3e9(4K=in>2TF_V_hobfaJL<>|CzUB-=7kgI8iMgI#99ub^&8KY&TrrqE3gEmbukk(z zubOZv3>PKT`Whjo03ili4{EM%g!8;=uAHv7u?~DU*fY^sA8*mWLRX-Ce}Gqr!gaPB zrU8mX(RcjZpc#J2=47qf%mj`k(5t_EZT#9Kw957vaGLi9C&?lP-eJB`g4c@L&VD@h zUwZYE#|LlDybYh!zG^jp*!!85=51B!1-ex8n_Jqc@1wNs&O;`?b>^3{Y@F>zvV?8x z_q8d#;T$UNq-}VhBIRoR&8N{`Zfh~jYonKUcm>e7D zzVyzneGxH-Y66*Zybj;f4ozA$Lz_r@GM1++F-_*L8E=h{)kb+n&=-w|I;WS%2$D3i zn`r@~6RKueHErrJ_@^5koL*!E$7^n6P^(fl7yjltmiI2j98hu~mF9mUW<{8jNi#ic zsH}$E4r5?O=~6D38g{xd0v@v$U%Cd)zUvymyxE-_qM!51z;+p^YUd=D zWBUI48EE3?p|!&z2~x^+ax-_X6m#W(a;V;3j;huC6Vcw6bju$sw*M72xIv+nMRx;bgu8M2-ryJQOT4f+oPbKQ_sSk(3nbfFDwl zi`+O_sq5hJTsf;y7Xv4)op)w=|^hLLF@h*22obr`? z9_PjA1TFLJF97HwGlob)hR{j<*uOENSusb1k@qS;-i_nam3@c~v^tG6(=+OA1Az4eOHY^do?cW9NKx z_8^T~irV2)lMVtibzxK3^TgOEVSZzQU!b`q$aSit)mxPZ$i|8Kqc%6@=; zD15Z8R3LUIJJcfR^zC-fYej$M>3RY{V)f8>f9@|G4>UrV_03|Y0U;5(Qn=uL^3$Bl!N_ zyX)#mn^XUbk`i|=Xc!V8r8_-vS!w9i1{uY#Umdw!l8hoQxFEn9MXEBda(b*ecjhFN3g8_?~%F?CpP8Ux~ zV6Z4notjWTi|yZQex5xi)owK~@#gc^1JO0vT^DV>^S^mOK<>?bpS-ZjMEypB!&ehE z4hs>CsCICM{~5n_iZPiC7+Bo;b`1(&0PixgX1C`qPkN4v2JPiHdrs$xxc-YAScg1i zbwMYdxxb@l8$eg~ZnnD$PB~rr!Ir7mY4ooDH(Me~s~yU)%KBBhT_LYn{QCP_Sk2kL zJk2qvs;`jVA;PmQC761( z1C@sQt`E+wS>pH{UPv@^o1cY|Cbne2fne@fI36uF%+sQJH4@bG2Gj_Ex;23y(wjry z1pkKBfYm#4Uxb%qeoj6Qg#qWno0ZEWb=Mu!nuFib6=L-S>Ans`QA^U;OFg-!5NE9; zHf-3}%@lLj7nNz-C1=sM8&PNep+t5{J9HMzZXiv^iB-qPWg&bsm~EZ)5*|*Ac0c!! zKfJKbHH-S%6^+z02rTN-KNhORsy*VWR8+S_^X??O>O(UW8iKob9W(5o9`H-&9hxN9kIHQ6d ztJO}-m`tmEmi$SQZS=2~n6r5n1*~dE@#{AX|Lbgp#l?v1D9A-WA}eP!jj`8@SdMY@ zwZx@iY$>^7Ly|w*YqD_3Hfo50)OPYo`(jW4cS)zvPB1h~=jZJ*5h zP3?tFV2-ld-_E7qdg(gpQBEo)Fq<4>#E07}_C3#ndGm!pp7m>3W#Q;43ZY679tnPU zi*PbFy5j*`m>JbpPufI2=4GQnO>f+sN;g8w?TsR6x-{czs$*;~SA073rG+lcs!)h; zBk|E+o?p9C2E6eK#OSp#?lL3-CSR0>c1{gva*VbWMs2oFML zSHZw%xksG9P2P6)-ZPe?p9FvxAkq9hjdpOx@+IG$OYw{=Ze@_Il@UmNZ|NTL;SCll z*%5grNKb~rouT{_YlQzz$@?9X!V|e=R$&wU(SgAb4RYN)R6x&c4`3kQ0%RK(+M|<| zt7RYO!*0EH-9HZx5TQl&#DURERx0_-WdL6LlfMR2y>wR>FKj&FH5$_6^c zovrLm7f)q6DDG}szQ@4vpmq#h2htwh53uSV9k=yovfJW+g<4V%Yc$vx*B{KcgNs8< z63{iSNoLgPb#`Q#`j+PcdTCIU2Qh!yRn@g|V61|8qBMr4!f<*`6QC;y2Reb<(hvHs z#waO*FJzcFj{1BGN@u!?Rvq(gD-afYygLmJZzgZ_I-ny z-yYgv9y!@Y7Dn&)ZQxcpVW>8W`+tZE(^!vm#;5_qn^5kKD17UsLpXP>kg)Bbwy zG0NCy?=*)_%ss5ru!uraw~=5c=YsbJlVlBu+&~(FF#Gtgeje>P*@G}=L74kK8Yp?e zU>tDX=r&K+6M z^!14w4X-}2gf0jiT~so<<0eW_h~@q%-uKHR!4HUt+qmDbWiE_)qwfkU5s^+bbl8J$II`aE;&5r~wqFYSLQ`zAf6d z(`Nmd0F(7R5;YzK#K@48@KvU=qIAfvM*w;)%z*o)!asZrXIi4rGw^7BxxM__Z&BuA zqelSthNuGwdpVHn^>Y-+RHAv0PmaORCvQ?7kr}!%s(*T!^#(Kn?upHAss4KpCM>>j z1~@kJx!{3#4Ns5gG*KJ~bSO;Q6eu5q-1_v9|k7$D0wz>{=%_KiRrFRj!%D z`+>`3NW~gc3^!h1;6zIB-aeQhI{yl)>nazqNfNmNwR?c9rvrCG4 zQ1e0)2=d-814OUEJa*lE4Ug`)fvUO;17t|)*Mq9X+PfXH#VunU5_iNoRA)&!gM-`b zB`u0@YQv(yJSZ-l`CfMC=@dH?wq-$>W^sI7>}hpC(!`$N-y!M8-?_VuVZWx1wP&A>g2slxGPt; z_|d@$9(HNW<|2yY`t;vuhTJ>gr}CJB?G^*Q4fsM%e`7tqL-yCFl-L!j%L*MWtX@^y z-`-(}gLJiE{mi%%M9|f)m)FS@G+8bqYbzOH&qK)@hg! ziT3D)Vgo@!$%8_T)q-V(*qhp}Ru_bZpNe!~6z|Ic^V8#I#0^8M3d#|z17{97{J5bq zMvu=z;%Z4H&Q{kgjVo!%J7wm$ZvoMb>tF8Uh6%Q#6m#+lp)pU@!nStKwzAwQ>Xs)ZrFrXmm>&#w62Z@JG;WFq zwJ&<;B}4*!rI_3q8dMvxHA!X>Y-dWhnb@y&U_!-nP0+x5(D(I}#vbL^CnoRWGF4-T zA8HqZ44pk zQ}u<~l5AS^S_paXND=P1)&P!;84_LS8E33sUq!0m8~Eot0Zm2DLbQDx)p5gY_HT=Q zEdAJcvx02#%Oim*K-QiYlY5z2L^_k z+fkG<1(MF0N^(OCId=rf&argZx$9nAAG`%BUC!HW2lZfiZs&SzpTlx<){6tdZSy91 zu^L+R>8Ng6*ucK@nWh^=08o)A&bnXGni)MCc$i8?{SMq$B~KatZM%KJH` z*h+Dc#p4wOot(DwKx2gn=EsuPPBPPPhek@*v>e0YIQw@z%@Je!Xq{2~B%sltt(Jl6 z*~g`7;N)d#MNzE#q*bwTm7>_>w(N%rg`UtK^;YN}E8cFx3$a1x>UjTfP( z30`lX0w1qjJcvQ2;G|tJavk{I18F!CU46K4v9L!uy_%*qRrry>%HO4rP_KR>+T{eQGj!xyWZZS{y$-{SCzX&K~YT_i?* zwiq)SI>OUY%d8>qeHYMOZ)c6jGQ4_V+q7*ItCt;5hL#~e2h!FElahzWBtoUk=tavK+jEBAvoK}-L?NDUwMbZr*%}m-#jH9nv zJivqI55kaC?c9p;HV##Qeqgp#^<1EdvtFfFE(D$|rOb+1{W&cuA~ezeXZM}!{sl-Z zWI1|xN+&|5L&x0bfInYbTK8y#-INxcmpr`|w{^a7>*QNSEz%eJCs>{tSo9Jr;$HGt zsZ0{)j?Uthl#uauqbS1eQ@ckY*(9F`1DVNY)w&gBfI~sGGLA9zo;zLx(O*_(P9&vO zM4oEC2(PX2kE;%)@;Q`VW~`sWz7M=#L0Lkl?*nB&UY7z>9hlDKCTn-b%YK3asw8+S ze1ztu0z2EvW170X5z#sTIWRL=_D@-ZusRL)1)h;6Qlhgorfts5O0rsE^o1t!(UWo; zrvlntgzK;yYk1;ph5Cjnp{ba2 zVCfcD8UM|&eutWDS1ul1rD;LdAwC`s(Fe|Tg39+|K&?t?3|#?2=LAz9>hg7jKHvF^Z^Jr}__1;v}kRs}%-Q|RLSqVD@RgMX7P4Z|Z>?B&$2+cje=$(o+YMRqd8 z>KTwHGfUwCC$hK@Ro=tj>m_5K>w2rbYI=9x_3^fRqX9h9CC>kcbyFcuuOEyc1qICZ z*Jn3N&V4I1%BUSIe8{Wz==$LJMs-h?=*#DmrIOybYDUb^4|Ql zR|&Y0o%_5=gjX{AuI7()69xKG7CZ8g7B?G_=boeZcZo`uA86btO5;ftXOTjQgE6r*79WFj!*M7o8MgU*tOC-b@%vgQvB9_guT17KBQOcFL06A zrjTc?itWj~kFvS>g$d!X(Es@^%arK{@N9z~wL~#fDb5jC_-nPJH3osQ_^Ky)j>trrjt%zQ7Oue>3U@@zWZ zgJ;+Fj0bLiFm||3{Ii9?UM+9&#(Q$bCu(7-D#*IEUd}m-X>L_b^mc8C>Smu|T=*L& zFR*zN3;ra}MJwHN9mDxgcssL-ws`xw3kgYWAu_QFjzZpI?|r?3$Jfa7 z-ylW@#Vy!fe2QnwD1cDK4QW0Uc9>-_RxQLOs>jtKAXnWy_ws0!Nyi(jP-{zgaG~cC ze=vt{RtKlyq)VG@GWDsrI$3x-?%CuvN4Zdb!Zd0mz%tQaVGnt&o+|t1@@|TH#V5zg z7}Ko4BTYK%K5pQPm1uW;2|0)}D&ZSrE!bWlW(wi@uP$Y7ISSFUH@S}eGpj`8XH@jL zypFUEXO!7D)7XeLP8w(z32*-)CMm5#rL4)tg01;6NS)y5eq;MAJfuY=$yK!>NQ(CL z+qLK$2j)+}e^2!+Yr`gW;rS*k;e7DT@}e8|PA~v97-F=2CByk&-3MSL*K z)7sT0N~x>b>KZe+^t9Bl2CJsL;S^Wmb-7ju{VNi^yGO;zy&)2{p)t zr_O^>xc2a0m`C!Gd1OnW-MhqPE#!F2k3hPhfra95CB7HgL19k-p=-5^F^>#uvkKQE zu-iAiz!#cv!R3Rt(xfhz4^2ip9~SG^5CesfvCZU*Lp?bO>HS}9E9|YBbRwW#xc?aA z;ycRIG-#eVIe4`pg01w5SssoD&JC0@*sgP$2R=dA8ln(B)kPp2*T$1%Laiq?xcnot z?HQ|K5wQUZU}y>^EynAF^mrfyKw!?X*BA$`}O048u9Nh+cri#qi=PnqJp@5`ddx~r9YAlSWiWcQ1sQZu+kAPI0@IyN)pbpCjwXuKwO;)m2TnIVgbvL;%6OwzI)vWU zK4M!cz`Dhdkt@>};Fl~(G16Z2rWmn^q*lFu|45Ujd7Hn`7{A>sKP=3IT8BmmKYXfT0D-PfD*)^`o96CQJFS0F>#2 zNphjx0$r?}&0S#o$if?p(-%lnuJ&2L2N*?)Xs*rpl^2*rtUvJM8e3|c YT0kFhNMr=HE0000NbVXQn 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

    j5zN$1{(OBXUc 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

    6VHalNoIXV)+yWw#ZTUD z0D>nzM@rGzMzHfrlA}0h+!~IoJdsXoCYwW|wg<;3vxM2J#O{7V=N7x;mhdcBd*Vyt zru1?L=dq(}yRqRr#yMYe2L~A`DcCn|-1P;WHhtJ1u1#zi6w_ONeUzJ_avHJN>zHl z<>F=nPL6ih_BY4LQBo7dLnv*H3`QOA19jB0L zDYusOgkHW|o7#+R8AQ$B=MZ(%GtMyR)!0ehOFpM~rjDr*DvoxUUhd32WBI!GLLNC1 zioS^YZnrKg>rx5&;Vfq_qHgVjsq?S;HA>=iR0aLlCI_d8 ekDmab1AH|u=O literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/tabBook.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/tabBook.png new file mode 100644 index 0000000000000000000000000000000000000000..c931238ce2b743cb903ee4b71356942f00ac9667 GIT binary patch literal 1128 zcmeAS@N?(olHy`uVBq!ia0vp^4nXY9!3HD`tZS47QY`6?zK#qG>ra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztF#qy&aSW-rmGtNQ0R|UkaRwFv z?!*88_ZaXgD=TwPFk(n@RS;9T9+1#+jzwyg-2^%3;=}d|b-YKW|NsBLo_1E+q{yQLqSc&R*G9phoh{ZfMe;A&&`4chK3spii@*ZSXo<{ce)*Lc+h&1kwrk* z(Yiw$2ic$y`Q)3fw*dW5d#bCzA$M@}F+>4DHH#Yt`#noHF z*1(YD#Kmz;Fyx2vp+DRL4nE3TzBRK0gUxUucaI5!fP=Cr>j6dwhTb0x3>*st>IviOIjLW5fjm&12ukUk@zVz(M# zzzY;H{Q39s@$vN!-#yfi-`BwA{_nsK21XWch6@{RT#nsc7AtpdUj45xFSkn?r+s0Z zsh^On)9Od9V0afvf|Hb|K6R#MUItrKg_b{+r=$J3T$e|FRd6MBQ z+kzDN4OL%Xh34PeQ~7Pa5JO%Ozkx&1;rsjR>+LH(JgCagf6uc(zBi$cEwO>YiH~6| zD=?KT+xE0sks;y1=jZ3&H~#ZsOp2SapOGo!2*UIQg7$_?z=*9&Vgbg?pCcTgSYi}x zV37I<^1iYi0|QV!C=K-f=mZN3D}!QP-3=r@oe`D-U|KPBzl$zV8e#~}mRR!rjwI1^I?e+MYk_!-cgk7>?69z+6$ jcm-1pN|1o;VrEz%+j#imjF-TK&%og6>gTe~DWM4f!NRGQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/textEdit.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/textEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..1b601b2d63167ebc754f65aebf657dc952318d5e GIT binary patch literal 2870 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 DAB;k} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/toolbar.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/toolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..1a2dde5f2d4ee2e115c7d406f744a82cc282eeef GIT binary patch literal 223 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 KHEYxzu>S`T5&UBS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/window.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/window.png new file mode 100644 index 0000000000000000000000000000000000000000..a6db64820df8b507dc25499fb14580e439c2a00c GIT binary patch literal 9112 zcmZvBcT^MI_Vy%!gp!1gfRuoU6zRPuAjJj&=}n4CXo3`}LPF?L6%+*}6eTDkASy_U z0*ZV1FruJ!$q%$mtLd-mSv+0QOX#ao!6*o4>s06-lzKVl7D z&w!tcEO7AKU|XXdys!qDJB9!NJI~$+0^BSV001^^A7f)Yo)8!s7(xgP!W=a=#smci zp7ZfP3jh&Ax2}cT*bML|zi$qYDt@Q*K``p7f)a5>0VBd>C2$Td@CIl@p7c+lVvlff zK}@nKL`Z3|d1Z>HxHQ$B!<-ZEsgy>go-yq|784n{Ffp!W?5Sx?Ol0FNL-?gC1VKPp;q^Utx8ikkbPo^`TrU;c)Km~Xe7 zo&I52PG(qssI+MTfX>tHll~Su$J;j~EZ*>zrTtWuxcgEvP0%#;hT<`Nd$4Dw^Y=SN zm-s(fb?Cwk@J6f^mGO+Od2s%x zL^B)xi)u>$dh*YQ+txFHdsmw8qgf8;u2?|l2^15~;3$|B2P91{`L!-}L=wg#Q}?l0 z2c&1Hs;Xwx3t!8rr?k$->1?AeE$JUvy4ZR>dHzl6S+=W8p%=b7$Q+e1GSJu0dp*{J zV(7Iqv=$%!auDiFomkY?vpPa0ys2-}vTBUhRVinS*((VBcMx8m;vr=AEsopj3KJcgB|R6t}<$Z`KXL z%YI{itUW|P62jLB$h;PR z3=f;Z9UwpsVG&v=1>)NgoqE=JBEp?1ojm0ZZ-V^dWbzPKIYRm_Zfnl2LhQo8&u}#0 z4Dc9Uw2AQrP7ZFPh8}(@i)^B7u@jYRlV69~l~`p2>(M9h-i(h;{MN>DBEF(pr4qHj z4LQiVmP8&^4-zhfPti6OE$X3FqJf}T}xhTBFZ`NKh))h2g&mzomJz4IR z`e^qU`~pvXWD~C&dmwqe?a|UFI(LkI3ws>QFtg~8IZr6d#RPXFsE2BCe2-E6kqGk$ z6G`rWQv2HhiR<;L4mdrFCL`g4EeBp_uN+kjb(+GNRA$Ser-_N>R_*evh

    w_?S?ACe7rDrU7V%VpnDuYF{ia-Za0 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/icon-dropdownbar.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/icon-dropdownbar.png new file mode 100644 index 0000000000000000000000000000000000000000..bbde0016d333af6d7f37f6c98fa2fe4096d09d7f GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9c!3HGfPKx~mq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c6?J&JIEGZ*O8RsD0E5dE=4qCj(+pp{dbyBeR&<2H9GgNW zXG61(r>E-~E?KhFa6^Md)GR59SsOYNh1n%pn0$Cbn3~TkxMdxc64tn8SO2G>$&n$J zXS#*Qe*^wE7b6yzC(O64o~Cp5#4m~e|NoyiNdJ~Ig@JvlzQKNBA%9Qb)RcsjKmJ=I cm^lm>rdU+ZQtT}@1-gmB)78&qol`;+0M~#~(*OVf literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-dark.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e62442ed82ceb7f5a01f79270547f0634362917e GIT binary patch literal 322 zcmV-I0lof-P)n(A0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyciI ztdA2}4*Bob)6rdu01``A>)~S&;#xnhqG+m=8#@jgQz1)^x_Y@P>QOlf^>dA>e@?Yc zd|r%*2^`^GB*bSFRV{B#(;O2V0f(GCF;69 z9`yGFU;qFB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-list.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout-list.png new file mode 100644 index 0000000000000000000000000000000000000000..c97c2af67fa24de9db149c7eb4998c04b37c071d GIT binary patch literal 252 zcmV>DO7f zSgqBDAmC(PF-48kA?9@^sF66t#3d#I5`ch{dBo6A;+R^#=q&8a7~cOic(!3-pqRZQFuq*&4&eH|GXHuiJ>Nn~YUU}gyL z32`kbDEQC7@bSa@Pai)3)r^AC5ExM*5O}zs5$Iuw64!{5;QX|b^2DN4hJeJ(yb?V> z*ARs=V?AR-^XBq4pc-3G7sn6_|KtP}J_7>-!^4cqO()OvoJmZO31#P=eX&3?@MY_c x9F{Zk9+m9*c2WwZhmXqhcrZx3;%GR<$gs48`{DCNr+h)7>*?y}vd$@?2>|G#JZ1m@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout.png new file mode 100644 index 0000000000000000000000000000000000000000..39d35e326483be43229f6ae79a06d01b058efe1e GIT binary patch literal 302 zcmV+}0nz@6P)n(A0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy&`Cr=RCwC#);7+|nM5J&?u8!)0#j>cFxoH-$HRUdZMM6|{RZb^*y()f z`o6JPYGsq!=3f30dmsIB*oQ^RJn^SGxoZ6&YYjU@i?=kRA zcil(WEAGE~iE^6CY5IkJID`Nn6KHsr$5Vg-0Bvm*<;uE&F8}}l07*qoM6N<$f=^_F Ac>n+a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout_inner.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/inspector-style-rollout_inner.png new file mode 100644 index 0000000000000000000000000000000000000000..a472dff4ffd370704c2c0b41526a3f75cbf50f3e GIT binary patch literal 519 zcmV+i0{H!jP)XLW*OL>=)c z32CqozgQR&D#t~}?;fdyr*Qm){8khyno!Tm!9vO-ZV|f z3t&+KxminIq$ZLTtMEylA#?$xra0Hb7>xr4&#yI?SZhah3&^K{%4nwI1Y{8)^B4=3 zgpM>U0~DcFRd#L*Cb-BfJCc5h76wLXt~htd&UOYC0EKIGyDike61L+DG&XXAF6`BCnoI? zffjiPl1b3Gxr>FzYdQ&ljB~kINvclLe*cz3O-c7PMNLuLG(o?%l&m%PoyByWUelK` zx9decRQEMSO-}o28M7+DOov&XOACY7=$i<;lg=_ zXU|`PEnor(=(SLOB|#R;WZ%m_wOHu?byWdiBruWMykacC>9*-?N9_& z)HN9H+`NII7@PQkgNML@4ffjKe}6GFVH3kA4hf^bn3CAU@QFiQghlc%4h?^4Z4h;1 z&D6vQMNr?!gyG`3GZ>1oiJv}m4v|Hu0jO)xl`B`t(u}N+1yr7Z$|I0p;b8&wJ4P5` l5eJt?X=(qd7Ha?j1^|O7s1bv^)q4N{002ovPDHLkV1ny3ilqPm literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/menubar.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/menubar.png new file mode 100644 index 0000000000000000000000000000000000000000..353451a2707499c1bca998dfb5aa883f3a309629 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9c!3HGfPKx~mq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c6;1GTaSW-rmGtNQ0S1>T%+pf6ry2bD{lEX={r&%W&Ybb# z>GAR52@Jf*v!u(o?$-~-!>)V&F!9W{uS-w}`ePn2XKE+cz9d$`D+V2ntJn;Kbgy}E zgsge=|KI=g2jT)t?qvJ)^zbwrZ)i_kbMQ!eVZ!E|dwWpQB|(t42Qzj l)YoP_&PppKvl$rK7>;iYnvgHLK@;dU22WQ%mvv4FO#ow#TO9xZ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel.png new file mode 100644 index 0000000000000000000000000000000000000000..f1098d8956cf7f67c430081e237f7661475a8650 GIT binary patch literal 1199 zcmV;g1W@~lP)w3|#00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$K}keGRCwBA z{LjDuL<0)|0*Gb9uA_YT6ny>qnJYTb<3&gBR8LY3uPDymzI6Q#32tuQZ}`*!1P}{0 z!7JxaJDojoxaQl}&q9ohjQ^v7Vh46@nnJQ8fOzqY{-gi@|7Yal=6z>uVcVi(Xu1zw z9Y6q~IO5IA=MtOOELrvQ$9G;3ALIaP1UJZ>@87-%9@)3O_w=y?pM67O!bHR+U%=!6 z0*DF5czpk^`kE!Px5FGjD*!SdWDm$bm>56+fgSPr(?`Co>z1vcXJQ1|2eKE$1_&S~ z21W*kjVl+fqJJj&53v_$KR^JnJh*#H^Yh2|5(KpX6Bk)l;n4@O_raZ8S^xpWa^~2f zI$|uKg(E=V%<+S@00G4E>cvydfB*im5HgGyHRR}f_2QW(Kmf7)`Td)NNW+-PbObXY zd4K?70TN6^8pcGXBM9mP2%tgX2!H@$`TO@TQE^SII&$;@1kj)e34j0^6pjE0AW(EL z6B(?`WJU);eE zCM2R7`TvgrOLYwpKwp>I+_f>pq~SCqO;}!{y7#F)=X%K9IuX$w&7H46(16LQOoTmUo^X2vr&H@Brg zd&F0+TVQfMlS~-I|O7e$bNtT0yzSd zY+nvU&kJNP$bNtTVgkm~e_EvhV$ul6et-aC91ML?fB<5#FgO1l6cn@spQ18ges64O zm_@4L^$iVaFnyeyoUGW?tXsD(1|Wb~XyG$l7UBW`0mQ^G2mn9;4GKp91Q2Z!CJzsU z{_=$=b+Y1gu3tb^d^4(?5UyfGq|H zAf~^6|1knfF6IGA3yh2`K!-2_1Q3&qoc#L@8#Y7%tJZ(?bO6Xcki9ap^6voxh{@90 zX62VJUzmY)eB!TPzhr=zrj7xb53&bjAIM&i{Qv>P*wE16bKuaCMK7N}9n>c^~0+kb6Gm5H|fECGKP)i3JhHCmpD#oV|WM3xG zUXc9&0mKMv=q_BlRc2ya-OV50zq6sc`r7sDELxhHe@G1pT>3aTIe+CYOAY+Q6>Qd7yuZ!TktnBZm0kN N002ovPDHLkV1lN<0pkDw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanelProfiles.ed.cs b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanelProfiles.ed.cs new file mode 100644 index 000000000..ca03e2b60 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanelProfiles.ed.cs @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +singleton GuiControlProfile (NavPanelProfile) +{ + opaque = false; + border = -2; + category = "Editor"; +}; + + +singleton GuiControlProfile (NavPanel : NavPanelProfile) +{ + bitmap = "./navPanel"; + category = "Editor"; +}; + +singleton GuiControlProfile (NavPanelBlue : NavPanelProfile) +{ + bitmap = "./navPanel_blue"; + category = "Editor"; +}; + +singleton GuiControlProfile (NavPanelGreen : NavPanelProfile) +{ + bitmap = "./navPanel_green"; + category = "Editor"; +}; + +singleton GuiControlProfile (NavPanelRed : NavPanelProfile) +{ + bitmap = "./navPanel_red"; + category = "Editor"; +}; + +singleton GuiControlProfile (NavPanelWhite : NavPanelProfile) +{ + bitmap = "./navPanel_white"; + category = "Editor"; +}; + +singleton GuiControlProfile (NavPanelYellow : NavPanelProfile) +{ + bitmap = "./navPanel_yellow"; + category = "Editor"; +}; +singleton GuiControlProfile (menubarProfile : NavPanelProfile) +{ + bitmap = "./menubar"; + category = "Editor"; +}; +singleton GuiControlProfile (editorMenubarProfile : NavPanelProfile) +{ + bitmap = "./editor-menubar"; + category = "Editor"; +}; +singleton GuiControlProfile (editorMenu_wBorderProfile : NavPanelProfile) +{ + bitmap = "./menu-fullborder"; + category = "Editor"; +}; +singleton GuiControlProfile (inspectorStyleRolloutProfile : NavPanelProfile) +{ + bitmap = "./inspector-style-rollout"; + category = "Editor"; +}; +singleton GuiControlProfile (inspectorStyleRolloutListProfile : NavPanelProfile) +{ + bitmap = "./inspector-style-rollout-list"; + category = "Editor"; +}; +singleton GuiControlProfile (inspectorStyleRolloutDarkProfile : NavPanelProfile) +{ + bitmap = "./inspector-style-rollout-dark"; + category = "Editor"; +}; +singleton GuiControlProfile (inspectorStyleRolloutInnerProfile : NavPanelProfile) +{ + bitmap = "./inspector-style-rollout_inner"; + category = "Editor"; +}; +singleton GuiControlProfile (inspectorStyleRolloutNoHeaderProfile : NavPanelProfile) +{ + bitmap = "./inspector-style-rollout-noheader"; + category = "Editor"; +}; +singleton GuiControlProfile (IconDropdownProfile : NavPanelProfile) +{ + bitmap = "./icon-dropdownbar"; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_blue.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..435e607fa1e284cba29f90630d922e792fb3bb43 GIT binary patch literal 1224 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_white.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_white.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbeac17a923103631556f74673421ccd14083a0 GIT binary patch literal 1177 zcmV;K1ZMk*P)w3|#00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$D@jB_RCwBA z{LjDuL<0)|0*Gb9uA_YT6ny>qnJYTb<3&gBR8LY3uPDymzI6Q#32tuQZ}`*!1P}{0 z!7JxaJDojoxaQl}&q9ohjQ^v7Vh46@nnJQ8fOzqY{-gi@|7Yal=6z>uVcVi(Xu1zw z9Y6q~IO5IA=MtOOELrvQ$9G;3ALIaP1UJZ>@87-%9@)3O_w=y?pM67O!bHR+U%=!6 z0*DF5czpk^`kE!Px5FGjD*!SdWDm$bm>56+fgSPr(?`Co>z1vcXJQ1|2eKE$1_&S~ z21W*kjVl+fqJJj&53v_$KR^JnJh*#H^Yh2|5(KpX6Bk)l;n4@O_raZ8S^xpWa^~2f zI$|uKg(E=V%<+S@00G4E>cvydfB*im5HgGyHRR}f_2QW(Kmf7)`Td)NNW+-PbObXY zd4K?70TN6^8pcGXBM9mP2%tgX2!H@$`TO@TQE^SII&$;@1kj)e34j0^6pjE0AW(EL z6B(?`WJU);eE%_AJo^^e*)w(00onQrpza!NtO8j z-@m!Q&|tWD@uEy+Wu^V|=g--Id=$q31P}|XmIGPN#>V!4)v8rnR8&-6LmNC~r3183 zWnyM#W-u}`dbV@t&ZXC{Ul)&#j`sZZ>lYKqF(5Sn0mKAzfS{n@pEGC9EC4#-Ej291 z6+4=mny*3jfb0X=3lanfAaK01u&^*3IB;MwOpw+9WDm$bki8)L0Rjl@h~>+dZvzGy zT^s-dAp1b}g6szfAh73wLG*GUdR`!VLG}X#5EC#+{?jlG5R4g+{Qv>PI2ih(00G2e zV{QHW$dMyU2<3NRIWdcj{F#QNj}@B|8ylM#fB<5lk>^-RQ4A13Obmkn00hvWa0EaA z(I#Q?@IdGA--0Ad3M4 z2*n;0PSV@{rfi~KmakRsHwkqc6N?}RjYIh0w4g{tD>s@9w302s%z_3 ze*E~68Cc&Zo;r0(hSrV&nGdoDWFN?0ko^Dw#0YHE__%p^EqeO+As=#c4rL(i>QyEk zZEcbrhfg1*Y?Ke!V2y2-PRA rq_TyJo9Ba`vDsByT_ub%2_V1#axGEuc-l>L00000NkvXXu0mjfNeluD literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_yellow.png b/Templates/BaseGame/game/tools/editorClasses/gui/panels/navPanel_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..269d15abdb7d174eb06d32257d2caf2b71c1a2b3 GIT binary patch literal 1215 zcmV;w1VHw3|#00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$QAtEWRCwBA z{LjDuL<0)|0*Gb9uA_YT6ny>qnJYTb<3&gBR8LY3uPDymzI6Q#32tuQZ}`*!1P}{0 z!7JxaJDojoxaQl}&q9ohjQ^v7Vh46@nnJQ8fOzqY{-gi@|7Yal=6z>uVcVi(Xu1zw z9Y6q~IO5IA=MtOOELrvQ$9G;3ALIaP1UJZ>@87-%9@)3O_w=y?pM67O!bHR+U%=!6 z0*DF5czpk^`kE!Px5FGjD*!SdWDm$bm>56+fgSPr(?`Co>z1vcXJQ1|2eKE$1_&S~ z21W*kjVl+fqJJj&53v_$KR^JnJh*#H^Yh2|5(KpX6Bk)l;n4@O_raZ8S^xpWa^~2f zI$|uKg(E=V%<+S@00G4E>cvydfB*im5HgGyHRR}f_2QW(Kmf7)`Td)NNW+-PbObXY zd4K?70TN6^8pcGXBM9mP2%tgX2!H@$`TO@TQE^SII&$;@1kj)e34j0^6pjE0AW(EL z6B(?`WJU);eEu8=sz&~SXmm9I0mP`KrS%KhawaAQCP_*DU+wJ-2le&OKVbq2Ffx5hX812W zpY$fx+rxaI;wgW=*uLz&7-2K(pFKe7S2D2@RLAQo6H2eO=vjphHURjgZ7R4%<{ zVBi48B{wuazfsV%<78q6<_{yIThDgx{IT@N#Ab^-)4iFUN z{&VKcodqf?58px@KnuXZq^WuDHOL;2eIR>5q5uH|j&~LoCWZqCt}JF^;-Zn|=;?%u z31lD0UXc9&0R(o$^5tCHn3=ih;s6)`*$1*0WIsRv0n;5LgQn)SmjluB0@(|)A0U93 zSXh|<(=H7VP9q@u0Ro6|F!V(M0*J-N+W7a8BM+7k%I`p@<a3GNd8&3A18TVq>Ea z0}wzgwDB9v!nZ`J1_&S~hCu)T0%%Y;0w92BlQ4OBAoQ0nL@5UdAQlUA!#^iZJX%aR zVUkmUeIcmN!a^<g?2g~o@e;EM+ zh)G3F{JpcYLe#&1-~Q3l0U-N8_Nu6gzXu2)rs~>XD?fhx#%yGylz8ftrVOne12P|E z56C`{y&(Gm0*DdV!trtQa9H&8@k>7B<{ZjE+STh!I@+2fI}V>dP|HeMR{Ybh9S7n7 z0ti%2Y|Sn$Q(|Uj`3q|4V8aj}V3)&(@#zEEmjkpHWIsRvF@fCV@9g=ZxV~MPgOl?Y zUFuGtJw-tK{DAg?><0)Sc!?Pw?ER!|@;pIXC->R3bO6ZwcAz~8K>J`~00D$jfq7Wz z|MIZv&pLehUiPK4r?kI+{UY$--W?x?>(|SjJpFn}wh^jNUP)yO7dOubJ!7-0wz^6f dWfDMu0RY6}I6 and after char # + // %startChar in the passed string. + + %startTag = "<" @ %tag @ ">"; + %endTag = ""; + + %startTagOffset = strpos(%string, %startTag, %startChar); + + // Compensate for presence of start tag. + %startOffset = %startTagOffset + strlen(%startTag); + + // Ok, now look for end tag. + %endTagOffset = strpos(%string, %endTag, %startOffset - 1); + + // If we didn't find it, bail. + if(%endTagOffset < 0) + return ""; + + // Evil hack - store last found item in a global. + %this.lastOffset = %endTagOffset; + + // And get & return the substring between the tags. + %result = getSubStr(%string, %startOffset, %endTagOffset - %startOffset); + + // Do a little mojo to deal with " and some other htmlentities. + %result = strreplace(%result, """, "\""); + %result = strreplace(%result, "&", "&"); + + return %result; +} + +function RSSFeedObject::onDisconnect(%this) +{ + // Create collection and load cache. + %ret = constructRSSHeadlineCollection(); + %ret.loadFromFile( "RSSCache.cs" ); + + // Ok, we have a full buffer now, hopefully. Let's process it. + //echo(" - Got " @ $RSSFeed::lineCount @ " lines."); + + // We want the feed title and the first three headlines + links. + + // Feed title - get the first occurence in the string. + %title = %this.getTagContents($RSSFeed::requestResults, "title", 0); + %titleLink = %this.getTagContents($RSSFeed::requestResults, "link", 0); + + //echo(" - Feed title: '" @ %title @ "'"); + //echo(" - Feed link: '" @ %titleLink @ "'"); + + %newItems = ""; + + // Ok, get the first headlines, if any... + for( %i = 0; %i < $RSSFeed::maxNewHeadlines; %i++ ) + { + %headline[%i] = %this.getTagContents($RSSFeed::requestResults, "title", %this.lastOffset); + %headlineLink[%i] = %this.getTagContents($RSSFeed::requestResults, "link", %this.lastOffset); + + // Skip the content - it's not going to do anything but confuse us. + %garbage = %this.getTagContents($RSSFeed::requestResults, "content:encoded", %this.lastOffset); + %isNew = %ret.addHeadline( constructRSSHeadline( %headline[%i], %headlineLink[%i] ) ); + + if( %isNew ) + { + %newItems = true; + //echo(" - Headline #" @ %i @ " : '" @ %headline[%i] @ "'"); + //echo(" - Headline Link #" @ %i @ " : '" @ %headlineLink[%i] @ "'"); + } + } + + if( %this._callback !$= "" ) + { + %params = %ret; + + if( %newItems ) + %params = %params @ ", \"" @ %newItems @ "\""; + + eval( %this._callback @ "(" @ %params @ ");" ); + } + + %ret.writeToFile( "RSSCache.cs", false ); +} + +function RSSUpdate::initialize( %callback ) +{ + new TCPObject(RSSFeedObject); + RSSFeedObject._callback = %callback; + + RSSFeedObject.connect( $RSSFeed::serverName @ ":" @ $RSSFeed::serverPort ); +} + +function RSSUpdate::destroy() +{ + if(isObject(RSSFeedObject)) + RSSFeedObject.delete(); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/RSSNews/RSSStructs.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/RSSNews/RSSStructs.ed.cs new file mode 100644 index 000000000..dbb55af72 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/RSSNews/RSSStructs.ed.cs @@ -0,0 +1,153 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// RSS Feed integration structures +// I apologize in advance if this RSS reader is too restrictive with regard +// to tags/enclosures. I may revisit it at some point to add support + +//------------------------------------------------------------------------------ +// RSS Headline Item +//------------------------------------------------------------------------------ +function constructRSSHeadline( %headline, %link ) +{ + %ret = new ScriptObject() + { + class = "RSSHeadline"; + _headline = %headline; + _link = %link; + }; + + return %ret; +} + +function RSSHeadline::getHeadline( %this ) +{ + return %this._headline; +} + +function RSSHeadline::getLink( %this ) +{ + return %this._link; +} + +function RSSHeadline::sameAs( %this, %headline ) +{ + return ( strcmp( %this.toString(), %headline.toString() ) == 0 ); +} + +function RSSHeadline::toString( %this ) +{ + return %this.getHeadline() @ " ( " @ %this.getLink() @ " ) "; +} + +//------------------------------------------------------------------------------ + +function constructRSSHeadlineCollection() +{ + %ret = new ScriptObject() + { + class = "RSSHeadlineCollection"; + }; + + // Create sim group for it + %ret._simGroup = new SimGroup(); + + return %ret; +} + +function RSSHeadlineCollection::getObject( %this, %index ) +{ + %ret = %this._simGroup.getObject( %index ); + + if( !isObject( %ret ) ) + { + warn( "No such index in headline collection." ); + return -1; + } + + return %ret; +} + +function RSSHeadlineCollection::getCount( %this ) +{ + return %this._simGroup.getCount(); +} + +function RSSHeadlineCollection::addHeadline( %this, %headline, %skipReorder ) +{ + for( %i = 0; %i < %this.getCount(); %i++ ) + { + %obj = %this.getObject( %i ); + + if( %obj.sameAs( %headline ) ) + { + //echo( "cache hit headline: " @ %headline.toString() ); + return false; + } + } + + %this._simGroup.add( %headline ); + + if( !%skipReorder ) + %this._simGroup.bringToFront( %headline ); + + //echo( "adding headline: " @ %headline.toString() ); + + return true; +} + +function RSSHeadlineCollection::writeToFile( %this, %file ) +{ + $rssHeadlineCollection::count = %this.getCount(); + + for( %i = 0; %i < %this.getCount(); %i++ ) + { + %hdl = %this.getObject( %i ); + $rssHeadlineCollection::headline[%i] = %hdl.getHeadline(); + $rssHeadlineCollection::link[%i] = %hdl.getLink(); + } + + export( "$rssHeadlineCollection::*", %file, false ); +} + +function RSSHeadlineCollection::loadFromFile( %this, %file ) +{ + %this._simGroup.clear(); + + $rssHeadlineCollection::count = 0; + + %file = getPrefsPath(%file); + if (isFile(%file) || isFile(%file @ ".dso")) + exec( %file ); + + for( %i = 0; %i < $rssHeadlineCollection::count; %i++ ) + { + //echo( "[LD: " @ %i @ "] Headline: " @ $rssHeadlineCollection::headline[%i] ); + //echo( "[LD: " @ %i @ "] Link: " @ $rssHeadlineCollection::link[%i] ); + + %hdl = constructRSSHeadline( $rssHeadlineCollection::headline[%i], + $rssHeadlineCollection::link[%i] ); + + // This does negate the cache check, but that is ok -pw + %this.addHeadline( %hdl, true ); + } +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/contextPopup.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/contextPopup.ed.cs new file mode 100644 index 000000000..a3e49daf7 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/contextPopup.ed.cs @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +/// @class ContextPopup +/// @brief Create a Popup Dialog that closes itself when signaled by an event. +/// +/// ContextPopup is a support class that offers a simple way of displaying +/// reusable context sensitive GuiControl popups. These dialogs are created +/// and shown to the user when the <b>show</b> method is used. +/// +/// Once a Popup is shown it will be dismissed if it meets one of a few +/// criteria. +/// +/// 1. A user clicks anywhere outside the bounds of the GuiControl, specified by +/// the 'dialog' field on the object. +/// 2. Time Passes of (n)Milliseconds, specifed by the 'timeout' field on +/// the object. +/// +/// For example, if you wished to create a context dialog with a dialog you held in +/// a local variable named %myDialog you would create a new script object as such. +/// +/// +/// @code +/// %MyContextPopup = new ScriptObject() +/// { +/// class = ContextPopup; +/// superClass= MyCallbackNamespace; // Only Necessary when you want to perform logic pre/post showing +/// dialog = %%myDialog.getID(); +/// delay = 500; // Pop the Popup after 500 Milliseconds +/// }; +/// @endcode +/// +/// Now, if you wanted to show the dialog %%myDialog and have it dismissed when anything in the +/// background is clicked, simply call the following. +/// +/// +/// @code +/// %MyContextPopup.show( %positionX, %positionY ); +/// @endcode +/// +/// If you need to know more than show the dialog and hide it when clicked or time passes, ContextPopup +/// Provides callback methods that you may override for doing intermediate processing on a dialog +/// that is to be shown or is being hidden. For example, in the above script we created a Context Dialog Container +/// called @%myContextDialog with a superClass of <b>MyCallbackNamespace</b>. If we wanted to hide the cursor when +/// the dialog was shown, and show it when the dialog was hidden, we could implement the following functions. +/// +/// @code +/// function MyCallbackNamespace::onContextActivate( %%this ) +/// { +/// // Hide Cursor Here +/// } +/// function MyCallbackNamespace::onContextDeactivate( %%this ) +/// { +/// // Show Cursor Here +/// } +/// @endcode +/// +/// @field GuiControl Dialog The GuiControl dialog to be shown when the context dialog is activated + +function ContextDialogContainer::onAdd(%this) +{ + // Add to our cleanup group. + $EditorClassesGroup.add( %this ); + + %this.base = new GuiButtonBaseCtrl() + { + profile = ToolsGuiTransparentProfile; + class = ContextDialogWatcher; + parent = %this; + modal = true; + }; + + // Flag not active. + %this.isPushed = false; + + // Add to our cleanup group. + $EditorClassesGroup.add( %this.base ); + + return true; + +} + +function ContextDialogContainer::onRemove(%this) +{ + %this.Hide(); + + if( isObject( %this.base ) ) + %this.base.delete(); +} + + + +//----------------------------------------------------------------------------- +/// (SimID this, int positionX, int positionY) +/// Shows the GuiControl specified in the Dialog field at the coordinates passed +/// to this function. If no coordinates are passed to this function, the Dialog control +/// is shown using it's current position. +/// +/// @param this The ContextDialogContainer object +/// @param positionX The X Position in Global Screen Coordinates to display the dialog +/// @param positionY The Y Position in Global Screen Coordinates to display the dialog +/// @param delay Optional delay before this popup is hidden that overrides that specified at construction time +/// +//----------------------------------------------------------------------------- +function ContextDialogContainer::Show( %this, %positionX, %positionY, %delay ) +{ + if( %this.isPushed == true ) + return true; + + if( !isObject( %this.Dialog ) ) + return false; + + // Store old parent. + %this.oldParent = %this.dialog.getParent(); + + // Set new parent. + %this.base.add( %this.Dialog ); + + if( %positionX !$= "" && %positionY !$= "" ) + %this.Dialog.setPositionGlobal( %positionX, %positionY ); + + Canvas.pushDialog( %this.base, 99 ); + + // Setup Delay Schedule + if( isEventPending( %this.popSchedule ) ) + cancel( %this.popSchedule ); + if( %delay !$= "" ) + %this.popSchedule = %this.schedule( %delay, hide ); + else if( %this.Delay !$= "" ) + %this.popSchedule = %this.schedule( %this.Delay, hide ); + +} + +//----------------------------------------------------------------------------- +/// (SimID this) +/// Hides the GuiControl specified in the Dialog field if shown. This function +/// is provided merely for more flexibility in when your dialog is shown. If you +/// do not call this function, it will be called when the dialog is dismissed by +/// a background click. +/// +/// @param this The ContextDialogContainer object +/// +//----------------------------------------------------------------------------- +function ContextDialogContainer::Hide( %this ) +{ + if( %this.isPushed == true ) + Canvas.popDialog( %this.base ); + + // Restore Old Parent; + if( isObject( %this.Dialog ) && isObject( %this.oldParent ) ) + %this.oldParent.add( %this.Dialog ); +} + + + +// ContextDialogWatcher Class - JDD +// CDW is a namespace link for the context background button to catch +// event information and pass it back to the main class. +// +// onClick it will dismiss the parent +// onDialogPop it will cleanup and notify user of deactivation +// onDialogPush it will initialize state information and notify user of activation +function ContextDialogWatcher::onClick( %this ) +{ + if( isObject( %this.parent ) ) + %this.parent.hide(); +} + +function ContextDialogWatcher::onDialogPop( %this ) +{ + if( !isObject( %this.parent ) ) + return; + + %this.parent.isPushed = false; + + if( %this.parent.isMethod( "onContextDeactivate" ) ) + %this.parent.onContextDeactivate(); +} + +function ContextDialogWatcher::onDialogPush( %this ) +{ + if( !isObject( %this.parent ) ) + return; + + %this.parent.isPushed = true; + + if( %this.parent.isMethod( "onContextActivate" ) ) + %this.parent.onContextActivate(); + +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/core/zip/zipFile.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/core/zip/zipFile.ed.cs new file mode 100644 index 000000000..9cbaa66c6 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/core/zip/zipFile.ed.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function ZipObject::addPath( %this, %path, %pathInZip ) +{ + %beginPath = expandFilename( %path ); + %path = pathConcat(%path, "*"); + %file = findFirstFile( %path ); + + while(%file !$= "") + { + %zipRel = makeRelativePath( %file, %beginPath ); + %finalZip = pathConcat(%pathInZip, %zipRel); + + %this.addFile( %file, %finalZip ); + + %file = findNextFile(%path); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/expandos.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/expandos.ed.cs new file mode 100644 index 000000000..6d2804fe3 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/expandos.ed.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Called to setup the base expandos +function setupBaseExpandos() +{ + // FIXME TGEA doesnt currently have these due to the way it's built + return; + + setScriptPathExpando("tools", getExecutablePath() @ "/tools", true); + setScriptPathExpando("tool", getExecutablePath() , true); + setScriptPathExpando("toolResources", getExecutablePath() @ "/resources", true); + + setScriptPathExpando("core", getExecutablePath() @ "/core", true); + + // Remove the game expando so we can use this to reset expandos + removeScriptPathExpando("game"); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/fileLoader.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/fileLoader.ed.cs new file mode 100644 index 000000000..19456af2c --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/fileLoader.ed.cs @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function loadDirectory(%path, %type, %dsoType) +{ + if( %type $= "" ) + %type = "ed.cs"; + if( %dsoType $= "" ) + %dsoType = "edso"; + + %cspath = %path @ "/*." @ %type; + + // Because in a shipping version there will be no .cs files, we can't just + // find all the cs files and exec them. + + // First we find all the scripts and compile them if there are any + // In the shipping version, this wont find anything. + if( !$Scripts::ignoreDSOs ) + { + %dsoReloc = compileDirectory(%cspath); + + // Finally we find all the dsos and exec them instead + + // If the DSOs are relocated by the engine (which will be the case when + // running the tools) then we need to look for the scripts again. + + if(! %dsoReloc) + %dsopath = %path @ "/*." @ %type @ "." @ %dsoType; + else + %dsopath = %cspath; + } + else + %dsopath = %cspath; + + //error("Execing Directory " @ %dsopath @ " ..."); + %file = findFirstFile(%dsopath); + + while(%file !$= "") + { + //error(" Found File: " @ %file); + + // As we cant exec() a .dso directly, we need to strip that part from the filename + %pos = strstr(%file, "." @ %dsoType); + if(%pos != -1) + %csfile = getSubStr(%file, 0, %pos); + else + %csfile = %file; + + exec(%csfile); + %file = findNextFile(%dsopath); + } +} + +function compileDirectory(%path, %dsoPath) +{ + %saveDSOPath = $Scripts::OverrideDSOPath; + $Scripts::OverrideDSOPath = %dsoPath; + + %dsoReloc = false; + + %file = findFirstFile(%path); + + //error("Compiling Directory " @ %path @ " ..."); + while(%file !$= "") + { + //error(" Found File: " @ %file @ " (" @ getDSOPath(%file) @ ")"); + if(filePath(%file) !$= filePath(getDSOPath(%file))) + %dsoReloc = true; + + compile(%file); + %file = findNextFile(%path); + } + + $Scripts::OverrideDSOPath = %saveDSOPath; + + return %dsoReloc; +} + +function listDirectory(%path) +{ + %file = findFirstFile(%path); + + echo("Listing Directory " @ %path @ " ..."); + while(%file !$= "") + { + echo(" " @ %file); + %file = findNextFile(%path); + } +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnail.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnail.ed.cs new file mode 100644 index 000000000..6f302a9dc --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnail.ed.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// This file merely contains the base functionality for creating your own +// 'subclassed' script namkespaces that define the visual appearance of +// a thumbnail for a guiThumnailPopup list. +// +// All border creation and callback click functionality is also defined in +// this file and may be overriden in your namespaces provided that you +// properly invoke the Parent::onMethodName( %parameterList ) to all this +// base namespace to do it's dependent processing. + +//function GuiDefaultThumbnail::onAdd( %this ) +//{ + //// Nothing Here. +//} +// +//function GuiDefaultThumbnail::onRemove( %this ) +//{ + //// Nothing Here. +//} + +//----------------------------------------------------------------------------- +// Object Browser Item Default Behaviors +//----------------------------------------------------------------------------- +function GuiDefaultThumbnail::onClick( %this ) +{ + // Store data and hide the dialog. + if( isObject( %this.base ) ) + { + %this.base.item = %this; + %this.base.Hide(); + } +} + +function GuiDefaultThumbnail::onRightClick( %this ) +{ + // Nothing Here. +} + +function GuiDefaultThumbnail::onMouseLeave( %this ) +{ + // Nothing Here. +} + +function ObjectBrowserItem::onMouseEnter( %this ) +{ + // Nothing Here. +} + +function GuiDefaultThumbnail::onDoubleClick( %this ) +{ + // By Default if the base funcitonality is called + // in onClick, we will never get here. However, if + // you want to override this functionality, simply + // override onClick and don't call the parent. +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnailPopup.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnailPopup.ed.cs new file mode 100644 index 000000000..5098cf052 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiClasses/guiThumbnailPopup.ed.cs @@ -0,0 +1,224 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// ContextDialogContainer Class - Example Use +//----------------------------------------------------------------------------- +// +//%MyContextDialog = new ScriptObject() +//{ +// class = ContextDialogContainer; +// superClass = GuiThumbnailPopup; +// dialog = %myContainedDialog.getID(); +// thumbType = "GuiThumbnailClass"; +// listType = "StaticSpriteThumbs"; +//}; +// %MyContextDialog.show( %positionX, %positionY ); +// %MyContextDialog.hide(); +// +// NOTES +// +// - thumbType describes a script namespace that will be linked to the creation +// of the actual thumbs in the list. This allows you to override their display +// - listType describes a script namespace that will be linked to the creation +// of the list and will have refresh and destroy called on it when you need +// to add objects to the list. to add an object, call %this.AddObject on you +// get a refresh call. +// +// +//function MyCallbackNamespace::onContextActivate( %this ) +//{ +// echo("Dialog has been pushed onto canvas, clicking outside of it will pop it!"); +//} +//function MyCallbackNamespace::onContextDeactivate( %this ) +//{ +// echo("Dialog has lost it's context and has thus been popped from the canvas!"); +//} +// +// +// Object Hierarchy +// [%scriptObject] ScriptObject (GuiThumbnailPopup) +// .superClass (ContextDialogContainer) +// (%scriptObject)->[%dialogCtrl] +// | GuiScrollCtrl [%scrollCtrl] (GuiThumbnailArray) +// | GuiDynamicCtrlArrayCtrl [%objectList] (GuiThumbnailCreator) +// .superClass ( listType ) +// .thumbType = %thumbType +// .base = %this +// +function GuiThumbnailPopup::CreateThumbPopup( %this, %parent, %thumbType, %label ) +{ + %base = new GuiWindowCtrl() + { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "260 200"; + minExtent = "140 200"; + visible = "1"; + text = %label; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + }; + %scroll = new GuiScrollCtrl() + { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiScrollProfile"; + class = "GuiThumbnailArray"; + internalName = "thumbnailScroll"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "5 13"; + Extent = "250 178"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + hScrollBar = "alwaysOff"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "1"; + Margin = "6 2"; + thumbType = %thumbType; // Special Tag - Class of thumbObject + }; + %base.add(%scroll); + %objectList = new GuiDynamicCtrlArrayControl() + { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiScrollProfile"; + class = %this.listType; + superClass = "GuiThumbnailCreator"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "250 178"; + MinExtent = "64 64"; + canSave = "1"; + Visible = "1"; + internalName = "objectList"; + hovertime = "1000"; + colCount = "0"; + colSize = %this.thumbSizeX; + rowSize = %this.thumbSizeY; + rowSpacing = "2"; + colSpacing = "2"; + thumbType = %thumbType; // Special Tag - Class of thumbObject + base = %this; // Special Tag - Link to base class for hiding of dlg + }; + %scroll.add(%objectList); + %parent.add(%base); + + return %base; + +} + +function GuiThumbnailPopup::onAdd(%this) +{ + // Call parent. + if( !Parent::onAdd( %this ) ) + return false; + + if( %this.thumbType $= "" ) + %this.thumbType = "GuiDefaultThumbnail"; + + %this.Dialog = %this.createThumbPopup( %this.base, %this.thumbType, %this.label ); + + if( !isObject( %this.Dialog ) ) + { + warn("GuiThumbnailPopup::onAdd - Invalid Context Dialog Specified!"); + return false; + } +} + + + +function GuiThumbnailArray::onRemove(%this) +{ + %this.destroy(); +} + +function GuiThumbnailArray::onWake( %this ) +{ + // Find objectList + %objectList = %this.findObjectByInternalName("ObjectList"); + + if( !isObject( %objectList ) ) + return; + + %objectList.refreshList(); +} + +function GuiThumbnailArray::refreshList(%this) +{ + // Find objectList + %objectList = %this.findObjectByInternalName("ObjectList"); + + if( !isObject( %objectList ) ) + return; + + // Parent will clear + %objectList.destroy(); + +} + +function GuiThumbnailArray::destroy(%this) +{ + // Find objectList + %objectList = %this.findObjectByInternalName("ObjectList"); + + if( !isObject( %objectList ) ) + return; + + while( %objectList.getCount() > 0 ) + { + %object = %objectList.getObject( 0 ); + if( isObject( %object ) ) + %object.delete(); + else + %objectList.remove( %object ); + } +} + +//----------------------------------------------------------------------------- +// Add a T2D Object to the Object List +//----------------------------------------------------------------------------- +function GuiThumbnailCreator::AddObject( %this, %object, %data, %tooltip ) +{ + // Add to group + $LB::ObjectLibraryGroup.add( %object ); + + // Build Object Container + %container = new GuiControl() { profile = ToolsGuiButtonProfile; }; + + // Add to list. + %this.add( %container ); + + // Return Container + return %container; +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormClass.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormClass.ed.cs new file mode 100644 index 000000000..8946280a4 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormClass.ed.cs @@ -0,0 +1,616 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +// This function will build out an empty frame and add it to its given parent. The newly +// built frame control is returned +function GuiFormClass::BuildEmptyFrame(%pos, %ext, %columns, %rows, %parentID) +{ + %frame = new GuiFrameSetCtrl() + { + profile = "ToolsGuiFrameSetProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = %pos; + extent = %ext; + columns = %columns; + rows = %rows; + borderWidth = "5"; //"4"; + //borderColor = "192 192 192"; + absoluteFrames = "1"; + relativeResizing = "1"; + specialHighlighting = "1"; + }; + + %parentID.add(%frame); + + return %frame; +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +// This function will build out a form control and add it to its parent. The newly built +// form control is returned. +function GuiFormClass::BuildFormControl( %parentID, %ContentLibrary ) +{ + // Find Default 'None' Content. + %contentNoneObj = GuiFormManager::FindFormContent( %ContentLibrary, "Scene View" ); + if( %contentNoneObj == 0 ) + { + error("GuiFormClass::BuildFormControl - Cannot find 'Scene View' Content Object!" ); + return false; + } + + %newFormObj = new GuiFormCtrl() + { + class = "FormControlClass"; + profile = "ToolsGuiFormProfile"; + canSaveDynamicFields = 1; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 455"; + minExtent = "20 20"; + visible = "1"; + caption = $FormClassNoContentCaption; + collapsable = "1"; + barBehindText = "1"; + hasMenu = true; + ContentLibrary = %ContentLibrary; + ContentID = %contentNoneObj; + Content = "Scene View"; + }; + %parentID.add( %newFormObj ); + + return %newFormObj; +} + + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +function FormControlClass::onWake( %this ) +{ + if( %this.ContentLibrary !$= "" && %this.Content !$= "" ) + { + %contentObj = GuiFormManager::FindFormContent( %this.ContentLibrary, %this.Content ); + if( %contentObj == 0 ) + { + error("GuiFormClass::onWake - Content Library Specified But Content Not Found!" ); + return; + } + + // Set Form Content + //if( %this.ContentID != %contentObj || !isObject( %this.ContentID ) ) + GuiFormClass::SetFormContent( %this, %contentObj ); + } + + %parentId = %this.getParent(); + %extent = %parentID.getExtent(); + %this.setExtent( GetWord(%extent, 0), GetWord(%extent, 1) ); + + GuiFormClass::BuildFormMenu( %this ); + +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +function GuiFormClass::BuildFormMenu( %formObj ) +{ + + if( !%formObj.hasMenu ) + return; + + // Menu Name. + %menuName = "FormMainMenu"; + + // Retrieve Menu ID. + %formMenu = %formObj.getMenuID(); + + %formMenu.clearMenuItems( %menuName ); + + //*** Setup the check mark bitmap index to start at the third bitmap + %formMenu.setCheckmarkBitmapIndex(1); + + //*** Add a menu to the menubar + %formMenu.addMenu( %menuName, %formObj); + %formMenu.setMenuBitmapIndex( %menuName, 0, true, false); + %formMenu.setMenuMargins(0, 0, 0); + + // Build Division Control Menu Items. + %formMenu.addMenuItem( %menuName, "Split This View Horizontally", 1); + %formMenu.addMenuItem( %menuName, "Split This View Vertically", 2); + %formMenu.addMenuItem( %menuName, "-", 0); + %formMenu.addMenuItem( %menuName, "Remove This View", 3); + +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +function GuiFormClass::SetFormContent( %formObj, %contentObj ) +{ + + // Menu Name. + %menuName = "FormMainMenu"; + + // Validate New Content. + if( !isObject( %contentObj ) ) + { + // Failure + error("GuiFormClass::SetFormContent- No Valid Content Object!" ); + return 0; + } + + // Remove any other content from the Form + %count = %formObj.getCount(); + if(%count > 1) + { + // Notify Script of Content Changing. + if( %formObj.isMethod("onContentChanging") ) + %formObj.onContentChanging( %contentObj ); + + // Object 0 is Always The Menu. + %currentContent = %formObj.getObject( 1 ); + if( isObject( %currentContent ) ) + { + // Remove from Reference List. + if( %formObj.contentID !$= "" ) + GuiFormManager::RemoveContentReference( %formObj.ContentLibrary, %formObj.contentID.Name, %currentContent ); + + // Delete the content. + %currentContent.delete(); + + // Update form Caption + %formObj.setCaption( $FormClassNoContentCaption ); + + } + } + + // Handle the Form content choices by obtaining the script build command + if( %contentObj.CreateFunction !$= "" ) + { + //*** Set the name first + %name = %contentObj.Name; + %formObj.setCaption(%name); + + // We set the content ID prior to calling the create function so + // that it may reference it's content to retrieve information about it. + %oldContentId = %formObj.contentID; + %formObj.contentID = %contentObj; + + %result = eval( %contentObj.CreateFunction @ "(" @ %formObj @ ");" ); + if(!%result) + { + //*** Couldn't set up the form's contents so set the name back. We need to + //*** do it like this to allow the form's contents to change the form caption + //*** if the above command worked. + %formObj.setCaption($FormClassNoContentCaption); + + // Restore Content ID. + %formObj.contentID = %oldContentID; + + // Notify Script of Failure. + if( %formObj.isMethod("onContentChangeFailure") ) + %formObj.onContentChangeFailure(); + } + else + { + // Add to Reference List. + %currentContent = %formObj.getObject( 1 ); + if( isObject( %currentContent ) ) + GuiFormManager::AddContentReference( %formObj.ContentLibrary, %contentObj.Name, %currentContent ); + + %formObj.Content = %formObj.contentId.name; + + // Notify Script of Content Change + if( %formObj.isMethod("onContentChanged") ) + %formObj.onContentChanged( %contentObj ); + + return %result; + } + } + return 0; +} + + +// +// Create a given content library content instance on a given parent control and +// reference count it in the ref manager. +// +function GuiFormClass::SetControlContent( %controlObj, %contentLibrary, %contentObj ) +{ + // Validate New Content. + if( !isObject( %contentObj ) ) + { + // Failure + error("GuiFormClass::SetControlContent- No Valid Content Object!" ); + return 0; + } + + // Remove any other content from the Form + if( isObject( %controlObj.ContentID ) ) + { + // Find Control of current content internal name on the control. + %currentContent = %controlObj.findObjectByInternalName( %controlObj.ContentID.Name ); + + if( isObject( %currentContent ) ) + { + + // Notify Script of Content Changing. + if( %controlObj.isMethod("onContentChanging") ) + %controlObj.onContentChanging( %contentObj ); + + // Remove from Reference List. + GuiFormManager::RemoveContentReference( %contentLibrary, %controlObj.contentID.Name, %currentContent ); + + // Delete the content. + %currentContent.delete(); + + } + } + + // Handle the Form content choices by obtaining the script build command + if( %contentObj.CreateFunction !$= "" ) + { + %name = %contentObj.Name; + + // We set the content ID prior to calling the create function so + // that it may reference it's content to retrieve information about it. + %oldContentId = %controlObj.contentID; + %controlObj.contentID = %contentObj; + + %currentContent = eval( %contentObj.CreateFunction @ "(" @ %controlObj @ ");" ); + if( !isObject( %currentContent ) ) + { + // Restore Content ID. + %controlObj.contentID = %oldContentID; + + // Notify Script of Failure. + if( %controlObj.isMethod("onContentChangeFailure") ) + %controlObj.onContentChangeFailure(); + } + else + { + // Add to Reference List. + GuiFormManager::AddContentReference( %contentLibrary, %contentObj.Name, %currentContent ); + + // Store Internal Name + %currentContent.setInternalName( %contentObj.Name ); + + // Store Content + %controlObj.Content = %controlObj.contentId.name; + + // Notify Script of Content Change + if( %controlObj.isMethod("onContentChanged") ) + %controlObj.onContentChanged( %contentObj ); + + return %currentContent; + } + } + return 0; +} + +// +// Remove a given library content instance from a control that is housing it. +// +function GuiFormClass::ClearControlContent( %controlObj, %contentLibrary ) +{ + + // Remove any other content from the Form + if( isObject( %controlObj.ContentID ) ) + { + // Find Control of current content internal name on the control. + %currentContent = %controlObj.findObjectByInternalName( %controlObj.ContentID.Name ); + + if( isObject( %currentContent ) ) + { + + // Notify Script of Content Changing. + if( %controlObj.isMethod("onContentClearing") ) + %controlObj.onContentClearing( %controlObj.ContentID ); + + // Remove from Reference List. + GuiFormManager::RemoveContentReference( %contentLibrary, %controlObj.contentID.Name, %currentContent ); + + // Delete the content. + %currentContent.delete(); + } + } +} + + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +// Turn a form into a split frame and add the form back and build a new blank +// form in the empty slot. %horizontal==ture then make a horizontal split, otherwise +// make a vertical one. +function GuiFormClass::AddFrameSplitToForm(%formid, %horizontal) +{ + %formparent = %formid.getGroup(); + + // Get form position and size + %pos = %formid.position; + %ext = %formid.extent; + %rows = "0"; + %columns = "0"; + if(%horizontal) + { + %framesplit = getWord(%ext,1) / 2; + %rows = "0 " @ %framesplit; + } + else + { + %framesplit = getWord(%ext,0) / 2; + %columns = "0 " @ %framesplit; + } + + // If the form's parent is a frame control and this form is the first control then + // we will need to move it to the front of the other children later on. Otherwise + // we'll be added to the bottom of the stack and the order will get messed up. + // This all assumes that a frame control only has two children. + %secondctrl = -1; + if(%formparent.getClassName() $= "GuiFrameSetCtrl") + { + //error("Form parent is GuiFrameSetCtrl"); + if(%formparent.getObject(0) == %formid) + { + // This form is the first child. + //error("Form is at the top"); + %secondctrl = %formparent.getObject(1); + } + } + + // If we're adding a frameset around the layout base, propogate the + // layoutRef and layoutObj's to the new parent. + if( %formID.LayoutRef !$= "" ) + %LayoutRef = %formID.LayoutRef; + else + %LayoutRef = 0; + + + // Remove form from parent, put a frame control in its place, and then add this form back to the frame + %formparent.remove(%formid); + %frame = GuiFormClass::BuildEmptyFrame(%pos, %ext, %columns, %rows, %formparent); + + if( %layoutRef != 0 ) + { + %frame.LayoutRef = %LayoutRef; + %frame.LayoutRef.layoutObj = %frame; + %frame.setCanSave( true ); + } + if(%secondctrl != -1) + { + // Move this frame to the top of its parent's children stack by removing the + // other child and adding it back again. This will force this second child to + // the bottom and our new frame back to the first child (the same location the + // original form was). Whew! Maybe the frame control needs to be modified to + // handle this. + //error("Moving to the top"); + %formparent.remove(%secondctrl); + %formparent.add(%secondctrl); + } + %frame.add(%formid); + + // Add a blank form to the bottom frame + GuiFormClass::BuildFormControl(%frame, %formid.ContentLibrary ); + + //error("New parent: " @ %frame SPC "(" @ %frame.getClassName() @ ")"); +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +//*** Remove a form's frame and any other of the frame's children and put the given +//*** form in its place, effectively removing the split. %keepform==true then remove +//*** all children of a parent Frame Set and keep the given Form. Otherwise, remove +//*** the given Form and keep the other child. +function GuiFormClass::RemoveFrameSplit(%formid, %keepform) +{ + //*** Get the form's parent and make sure it is a frame GUI. Other wise do nothing. + %frameID = %formid.getGroup(); + if(%frameID.getClassName() !$= "GuiFrameSetCtrl") + return; + + //*** Get frame's position and size + %pos = %frameID.position; + %ext = %frameID.extent; + + if(%keepform) + { + %form = %frameID.getObject(0); + + // Remove from Reference List. + if(%form.getClassName() $= "GuiFormCtrl") + GuiFormManager::RemoveContentReference( %form.ContentLibrary, %form.contentID.Name, %form.getObject(1) ); + + //*** Go through the frame's children and remove them (which includes our form) + %frameID.clear(); + } + else + { + + // Remove from Reference List. + if( %formId.getClassName() $= "GuiFormCtrl" ) + GuiFormManager::RemoveContentReference( %formId.ContentLibrary, %formId.contentID.Name, %formId.getObject(1) ); + + //*** Store the first child that is not the given Form + %count = %frameID.getCount(); + for(%i=0; %i < %count; %i++) + { + %child = %frameID.getObject(%i); + if(%child != %formid) + { + //*** This is the first child that isn't the given Form, so + //*** swap the given %formid with this new child so we keep it + %formid = %child; + break; + } + } + + //*** Now remove all children from the frame. + %frameID.clear(); + } + + //*** Obtain the frame's parent + %frameparentID = %frameID.getGroup(); + + //*** If the frame's parent is itself a frame, then track all of its children and add + //*** our form into the correct location, and remove our frame in the process. Otherwise + //*** just delete our frame and add our form to the parent. + if(%frameparentID.getClassName() $= "GuiFrameSetCtrl") + { + //*** Store the children + %count = %frameparentID.getCount(); + %check = -1; + for(%i=0; %i<%count; %i++) + { + %obj[%i] = %frameparentID.getObject(%i); + if(%obj[%i] == %frameID) + %check = %i; + } + + //*** Clear the existing children + %frameparentID.clear(); + + //*** Now add them back, including our form + for(%i=0; %i<%count; %i++) + { + if(%i == %check) + { + //*** Add our form + %frameparentID.add(%formid); + + } + else + { + //*** Add the old child back + %frameparentID.add(%obj[%i]); + } + } + + } else + { + // If we're about to remove a frame that has a layout ref move it to the new object (%formID) + if( %frameID.LayoutRef !$= "" ) + { + %formID.LayoutRef = %frameID.LayoutRef; + // By default if a control has been added to a form it will tag itself as cannot save. + // just to be safe, we mark it as we can since it's clearly now becoming the root of a layout. + %formID.LayoutRef.layoutObj = %formID; + %formID.setCanSave( true ); + } + //*** Remove old child and add our form to the parent + %frameparentID.remove(%frameID); + %frameparentID.add(%formid); + + } + + //*** Finally resize the form to fit + %formid.resize(getWord(%pos,0),getWord(%pos,1),getWord(%ext,0),getWord(%ext,1)); +} + + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +//*** Will resize the Form's content to fit. This is usually done at the beginning when +//*** the content is first added to the form. +function FormControlClass::sizeContentsToFit(%this, %content, %margin) +{ + %formext = %this.getExtent(); + %menupos = %this.getObject(0).getPosition(); + %menuext = %this.getObject(0).getExtent(); + + %ctrlposx = getWord(%menupos,0) + %this.contentID.Margin; + %ctrlposy = getWord(%menupos,1) + getWord(%menuext,1) + %this.contentID.Margin; + %ctrlextx = getWord(%formext,0) - %ctrlposx - %this.contentID.Margin; + %ctrlexty = getWord(%formext,1) - %ctrlposy - %this.contentID.Margin; + + %content.resize(%ctrlposx,%ctrlposy,%ctrlextx,%ctrlexty); +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +function FormControlClass::onResize(%this) +{ + //*** If this form has a content child, then pass along this resize notice + //*** to allow it to do something. + if(%this.getCount() > 1) + { + %child = %this.getObject(1); + if( isObject( %child ) ) + %this.sizeContentsToFit( %child ); + + if( %child.isMethod("onResize") ) + %child.onResize(%this); + } +} + +// +// This will change to something more abstract in the near future, or will be moved into +// the level editor if there is no concise way to abstract it. +// +function FormMenuBarClass::onMenuItemSelect(%this, %menuid, %menutext, %itemid, %itemtext) +{ + %formId = %menuid; // %menuid should contain the form's ID + + //error("FormMenuBarClass::onMenuSelect(): " @ %menuid SPC %menutext SPC %itemid SPC %itemtext SPC "parent: " @ %formparent); + + // If the ID is less than 1000, we know it's a layout menu item + if( %itemid < 1000 ) + { + // Handle the standard menu choices + switch(%itemid) + { + case "1": // Add horizontal split + GuiFormClass::AddFrameSplitToForm(%formid, true); + + case "2": // Add vertical split + GuiFormClass::AddFrameSplitToForm(%formid, false); + case "3": // Remove split and keep other child + GuiFormClass::RemoveFrameSplit(%formid, false); + } + + // We're Done Here. + return; + } + else + GuiFormClass::SetFormContent( %formId, %itemId ); + + +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormContentManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormContentManager.ed.cs new file mode 100644 index 000000000..5af210109 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormContentManager.ed.cs @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +$FormClassNoContentCaption = "None"; + +//----------------------------------------------------------------------------- +// Add Form Content to a Library or Update if it Already Exists +// +// Returns : Content ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::AddFormContent( %library, %contentName, %contentCreate, %contentSave, %contentMargin ) +{ + // See if we were passed a library ID. + if( !isObject( %library ) ) + %libraryObj = GuiFormManager::FindLibrary( %library ); + else + %libraryObj = %library; + + // See if we Found the Library. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + { + error( "GuiFormManager::AddFormContent - Unable to Find Library by Name or ID!" ); + return 0; + } + + // See if this reference already exists. + %contentRef = GuiFormManager::FindFormContent( %libraryObj, %contentName ); + + // If it exists, just update it's create/save functions. + if( %contentRef != 0 ) + { + // Echo Update. + //echo( "GuiFormManager::AddFormContent - Found Existent Content Reference, Updating Create/Save Functions" ); + + // Apply Update. + %contentRef.CreateFunction = %contentCreate; + %contentRef.SaveFunction = %contentSave; + %contentRef.Margin = %contentMargin; + + // Return Success. + return %contentRef; + } + + // Create Content Reference List. + %refList = new SimSet(); + + // Add Reference List to Library. + %libraryObj.getObject(0).add( %refList ); + + // Create Content Reference Object. + %newContentRef = new ScriptObject() + { + Name = %contentName; + CreateFunction = %contentCreate; + SaveFunction = %contentSave; + Margin = %contentMargin; + RefList = %refList; + }; + + // Add to library. + %libraryObj.add( %newContentRef ); + + // Return Success. + return %newContentRef; +} + +//----------------------------------------------------------------------------- +// Remove Form Content from a Library +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::RemoveFormContent( %library, %contentName ) +{ + // See if we were passed a library ID. + if( !isObject( %library ) ) + %libraryObj = GuiFormManager::FindLibrary( %library ); + else + %libraryObj = %library; + + // See if we Found the Library. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + return false; + + // See if this reference already exists. + %contentRef = GuiFormManager::FindFormContent( %libraryObj, %contentName ); + + // If it doesn't exist, just return success. + if( %contentRef == 0 || !isObject( %contentRef ) ) + return true; + + // Remove From Library. + %libraryObj.remove( %contentRef ); + + // Return Success. + return true; +} + + +//----------------------------------------------------------------------------- +// Find Form Content in a Library +// +// Returns : Content Reference Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::FindFormContent( %library, %contentName ) +{ + // See if we were passed a library ID. + if( !isObject( %library ) ) + %libraryObj = GuiFormManager::FindLibrary( %library ); + else + %libraryObj = %library; + + // See if we Found the Library. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + return 0; + + // Look for the content by name in our library. + for( %i = 0; %i < %libraryObj.getCount(); %i++ ) + { + %object = %libraryObj.getObject( %i ); + if( %object.Name $= %contentName ) + return %object; + } + + // Return Failure. + return 0; +} + +//----------------------------------------------------------------------------- +// Get Form Content in a Library by Index +// +// Returns : Content Reference Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::GetFormContentByIndex( %library, %index ) +{ + // See if we were passed a library ID. + if( !isObject( %library ) ) + %libraryObj = GuiFormManager::FindLibrary( %library ); + else + %libraryObj = %library; + + // See if we Found the Library. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + return 0; + + if( %index < %libraryObj.getCount() ) + return %libraryObj.getObject( %index ); + +} + + +//----------------------------------------------------------------------------- +// Get Form Content Count in a Library +// +// Returns : Number of content objects in this library or 0 +//----------------------------------------------------------------------------- +function GuiFormManager::GetFormContentCount( %library ) +{ + // See if we were passed a library ID. + if( !isObject( %library ) ) + %libraryObj = GuiFormManager::FindLibrary( %library ); + else + %libraryObj = %library; + + // See if we Found the Library. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + { + return 0; + } + + // Return Count. + return %libraryObj.getCount(); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLayoutManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLayoutManager.ed.cs new file mode 100644 index 000000000..cc449b24c --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLayoutManager.ed.cs @@ -0,0 +1,394 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Register a Content Library Layout +// +// Returns : Layout Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::InitLayouts( %libraryName, %layoutName, %layoutObj ) +{ + // Retrieve Library Object + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::RegisterLayout - Unable to find Library" SPC %libraryName ); + return 0; + } + + // Load up all Layouts in the layout base path. + loadDirectory( %libraryObj.basePath, "cs", "dso" ); + +} + +//----------------------------------------------------------------------------- +// Register a Content Library Layout +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::RegisterLayout( %libraryName, %layoutName, %layoutObj ) +{ + // Retrieve Library Object + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::RegisterLayout - Unable to find Library" SPC %libraryName ); + return false; + } + + // Retrieve Layout Group + %layoutGroup = %libraryObj.getObject( 1 ); + if( !isObject( %layoutGroup ) ) + { + error("GuiFormManager::RegisterLayout - Unable to locate layout group!"); + return false; + } + + // See if a layout with this name already exists. + if( GuiFormManager::FindLayout( %libraryName, %layoutName ) != 0 ) + { + error("GuiFormManager::RegisterLayout - Layout with name" SPC %layoutName SPC "already exists!"); + return false; + } + + %layoutRef = new ScriptObject() + { + layoutGroup = %layoutGroup; + layoutName = %layoutName; + layoutLibrary = %libraryObj; + layoutObj = %layoutObj; + layoutFile = %libraryObj.basePath @ %layoutName @ ".cs"; + }; + + // Tag Layout Object Properly so it can reset itself. + %layoutObj.layoutRef = %layoutRef; + + // Add Layout Object to group. + %layoutGroup.add( %layoutObj ); + + // Add Layout Object Ref to group. + %layoutGroup.add( %layoutRef ); + + // Return Success. + return true; +} + +//----------------------------------------------------------------------------- +// Unregister a Content Library Layout +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::UnregisterLayout( %libraryName, %layoutName, %deleteFile ) +{ + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::UnregisterLayout - Unable to find Library" SPC %libraryName ); + return false; + } + + // See if the layout exists. + %layoutObjRef = GuiFormManager::FindLayout( %libraryObj, %layoutName ); + + if( %layoutObjRef == 0 ) + return true; + + // Remove Layout File. + if( ( %deleteFile == true ) && isFile( %layoutObjRef.layoutFile ) ) + fileDelete( %layoutObjRef.layoutFile ); + + // Delete the Object. + if( isObject( %layoutObjRef.layoutObj ) ) + %layoutObjRef.layoutObj.delete(); + + // Delete the Reference + %layoutObjRef.delete(); + + // Layout Unregistered. + return true; + +} + +//----------------------------------------------------------------------------- +// Find a Content Library Layout +// +// Returns : Layout Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::FindLayout( %libraryName, %layoutName ) +{ + // Fetch Library Object. + if( isObject( %libraryName ) && %libraryName.Name !$= "" ) + %libraryName = %libraryName.Name; + + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + + if( %libraryObj == 0 ) + { + error("GuiFormManager::FindLayout - Unable to find Library" SPC %libraryName ); + return 0; + } + + // Retrieve Layout Group + %layoutGroup = %libraryObj.getObject( 1 ); + if( !isObject( %layoutGroup ) ) + { + error("GuiFormManager::FindLayout - Unable to locate layout group!"); + return 0; + } + + // Find Layout Object. + for( %i = 0; %i < %layoutGroup.getCount(); %i++ ) + { + %layoutGroupIter = %layoutGroup.getObject( %i ); + if( %layoutGroupIter.getClassName() $= "ScriptObject" && %layoutGroupIter.layoutName $= %layoutName ) + return %layoutGroupIter; + } + + // Not Found + return 0; +} + +//----------------------------------------------------------------------------- +// Save a Content Library Layout +// +// Returns : True or False +//----------------------------------------------------------------------------- +function GuiFormManager::SaveLayout( %library, %layoutName, %newName ) +{ + %libraryObj = GuiFormManager::FindLibrary( %library ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::SaveLayout - Unable to find Library" SPC %library ); + return false; + } + + %layoutObjRef = GuiFormManager::FindLayout( %library, %layoutName ); + if( %layoutObjRef == 0 ) + { + error("GuiFormManager::SaveLayout - Cannot find layout" SPC %layoutName ); + return false; + } + + // Do any form layout specifics saving. + GuiFormManager::SaveLayoutContent( %layoutObjRef.layoutObj ); + + %newFile = %libraryObj.basePath @ "/" @ %newName @ ".cs"; + if( %newName $= "" ) + { + %newName = %layoutObjRef.layoutName; + %newFile = %layoutObjRef.layoutFile; + } + + // Open Layout File Object. + %layoutFile = new FileObject(); + if( !%layoutFile.openForWrite( %newFile ) ) + { + error("GuiFormManager::SaveLayout - Unable to open" SPC %newFile SPC "for writing!"); + %layoutFile.delete(); + return false; + } + + // Get Layout Object + %layoutObj = %layoutObjRef.layoutObj; + + // Write Layout Object to File + %layoutFile.writeObject( %layoutObj, "%layoutObj = " ); + %layoutFile.writeLine("GuiFormManager::RegisterLayout(\"" @ %libraryObj.name @ "\",\"" @ %newName @ "\",%layoutObj);" ); + %layoutFile.close(); + %layoutFile.delete(); + + // Layout Saved + return true; + +} + +//----------------------------------------------------------------------------- +// Reload The Current Layout from the version last stored on disk. +// +// Returns : True or False +//----------------------------------------------------------------------------- +function GuiFormManager::ReloadLayout( %libraryName, %layoutName, %parent ) +{ + %layoutObj = GuiFormManager::FindLayout( %libraryName, %layoutName ); + if( %layoutObj == 0 || !isObject( %layoutObj ) ) + { + error("GuiFormManager::ReloadLayout - Unable to locate layout" SPC %layoutName SPC "in library" SPC %libraryName ); + return 0; + } + + // Store necessary layout info before the object is destroyed in UnregisterLayout. + %layoutFile = %layoutObj.layoutFile; + + // Unregister Layout but don't delete the layout file from disk. + if( !GuiFormManager::UnregisterLayout( %libraryName, %layoutName, false ) ) + { + error("GuiFormManager::ReloadLayout - Unable to unregister layout file" SPC %layoutFile ); + return 0; + } + + // Load the layout from disk. + exec( %layoutFile ); + + // Set it active. + GuiFormManager::ActivateLayout( %libraryName, %layoutName, %parent ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Activate a Layout on a Given Parent. +// +// Returns : True or False +//----------------------------------------------------------------------------- +function GuiFormManager::ActivateLayout( %library, %layoutName, %parent ) +{ + %libraryObj = GuiFormManager::FindLibrary( %library ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::FindLayout - Unable to find Library" SPC %library ); + return 0; + } + + %layoutObjRef = GuiFormManager::FindLayout( %library, %layoutName ); + if( %layoutObjRef == 0 ) + { + error("GuiFormManager::ActivateLayout - Cannot find layout" SPC %layoutName ); + return false; + } + + // Clear parent for new layout. + %parent.clear(); + + %layoutObj = %layoutObjRef.layoutObj; + + // Size to fit parent container. + %extent = %parent.getExtent(); + %layoutObj.setExtent( GetWord(%extent, 0), GetWord(%extent, 1) ); + + // Add to parent. + %parent.add( %layoutObj ); + + // Not Found + return true; +} + +//----------------------------------------------------------------------------- +// Deactivate a given layout. +// +// Returns : True or False +//----------------------------------------------------------------------------- +function GuiFormManager::DeactivateLayout( %library, %layoutName ) +{ + %libraryObj = GuiFormManager::FindLibrary( %library ); + if( %libraryObj == 0 ) + { + error("GuiFormManager::DeactivateLayout - Unable to find Library" SPC %library ); + return 0; + } + + %layoutObjRef = GuiFormManager::FindLayout( %library, %layoutName ); + if( %layoutObjRef == 0 ) + { + error("GuiFormManager::DeactivateLayout - Cannot find layout" SPC %layoutName ); + return false; + } + + // Retrieve Layout Group + %layoutGroup = %libraryObj.getObject( 1 ); + if( !isObject( %layoutGroup ) ) + { + error("GuiFormManager::RegisterLayout - Unable to locate layout group!"); + return 0; + } + + // Fetch Layout Object + %layoutObj = %layoutObjRef.layoutObj; + + // Clear all forms content. + GuiFormManager::ClearLayoutContent( %layoutObj ); + + // Return layout to it's home. + %layoutGroup.add( %layoutObj ); + + // Not Found + return true; +} + +//----------------------------------------------------------------------------- +// Recursively Remove Form Content +// +// Returns : None. +//----------------------------------------------------------------------------- +function GuiFormManager::SaveLayoutContent( %layoutObj ) +{ + for( %i = 0; %i < %layoutObj.getCount(); %i++ ) + { + %object = %layoutObj.getObject( %i ); + if( %object.isMemberOfClass( "SimGroup" ) ) + { + %formContent = 0; + if (%object.getCount() > 0) + %formContent = %object.getObject( 1 ); + + if( isObject( %formContent ) && %object.ContentLibrary !$= "" && %object.Content !$= "" ) + { + %contentObj = GuiFormManager::FindFormContent( %object.ContentLibrary, %object.Content ); + if( %contentObj == 0 ) + { + error("GuiFormManager::SaveLayoutContent - Content Library Specified But Content Not Found!" ); + return; + } + + if( %contentObj.SaveFunction !$= "" ) + eval( %contentObj.SaveFunction @ "(" @ %object @ "," @ %formContent @ ");" ); + } + } + else + GuiFormManager::SaveLayoutContent( %object ); + } +} + +//----------------------------------------------------------------------------- +// Recursively Remove Form Content +// +// Returns : None. +//----------------------------------------------------------------------------- +function GuiFormManager::ClearLayoutContent( %layoutObj ) +{ + for( %i = 0; %i < %layoutObj.getCount(); %i++ ) + { + %object = %layoutObj.getObject( %i ); + if( %object.getClassName() $= "GuiFormCtrl" ) + { + // Clear Content ID So that onWake recreates the content. + %object.ContentID = ""; + + %formContent = %object.getObject( 1 ); + if( isObject( %formContent ) ) + %formContent.delete(); + } + else + GuiFormManager::ClearLayoutContent( %object ); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLibraryManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLibraryManager.ed.cs new file mode 100644 index 000000000..e8918aa92 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormLibraryManager.ed.cs @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Register a Content Library +// +// Returns : Library Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::RegisterLibrary( %libraryName, %libraryBasePath ) +{ + %libraryPrepend = "GFCM"; + %newLibraryObjectName = %libraryPrepend @ %libraryName; + + // If the library already exists, just return it's object. + if( isObject( %newLibraryObjectName ) ) + return %newLibraryObjectName.getId(); + + // We must have the content manager to continue. + if( !isObject( FormContentManager ) ) + { + error("GuiFormManager::RegisterLibrary - Unable to find FormContentManager object!"); + return 0; + } + + // Create Content Library. + %newLibrary = new SimGroup( %newLibraryObjectName ) + { + Name = %libraryName; + }; + + // Expand Base Path + %libraryFullPath = getPrefsPath( %libraryBasePath ); + + // Store disk base path + %newLibrary.basePath = %libraryFullPath; + + // Ensure Path Exists + createPath( %libraryFullPath ); + + // Add Library to Content Manager. + FormContentManager.add( %newLibrary ); + + // Create Content Library Ref Group. + %newLibraryRefGroup = new SimGroup(); + %newLibraryRefGroup.setInternalName("RefGroup"); + %newLibrary.add( %newLibraryRefGroup ); + + // Create Content Library Layout Group. + %newLibraryLayoutGroup = new SimGroup(); + %newLibraryLayoutGroup.setInternalName("LayoutGroup"); + %newLibrary.add( %newLibraryLayoutGroup ); + + // Add Library to Content Manager. + FormContentManager.add( %newLibrary ); + + + // Add [none] Content. + GuiFormManager::AddFormContent( %libraryName, $FormClassNoContentCaption ); + + // Return Library Object. + return %newLibrary; + +} + +//----------------------------------------------------------------------------- +// Unregister a Content Library +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::UnregisterLibrary( %libraryName ) +{ + // Find Library Object. + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + + if( !isObject( FormContentManager ) || !isObject( %libraryObj ) ) + { + error("GuiFormManager::RegisterLibrary - Unable to find GuiFormManager or Library!"); + return false; + } + + // Remove all Content Reference Objects in this Library. + while( %libraryObj.getCount() > 0 ) + { + if( isObject( %libraryObj.getObject( 0 ) ) ) + %libraryObj.getObject( 0 ).delete(); + %libraryObj.remove( 0 ); + } + // Delete the library + %libraryObj.delete(); + + // Return Success. + return true; + +} + + +//----------------------------------------------------------------------------- +// Find a Content Library +// +// Returns : Library Object ID or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::FindLibrary( %libraryName ) +{ + // Generate Library Name. + %libraryObjectName = "GFCM" @ %libraryName; + + // Find Library by Name. + if( isObject( %libraryObjectName ) ) + return %libraryObjectName.getId(); + + // Didn't find by name, see if this is already a library ID. + if( isObject( %libraryName ) ) + return %libraryName; + + // Couldn't Find Library + return 0; +} + + +function GuiFormManager::Init() +{ + // Create SimGroup. + new SimGroup( FormContentManager ){}; +} + + +function GuiFormManager::Destroy() +{ + while( FormContentManager.getCount() > 0 ) + { + %object = FormContentManager.getObject( 0 ); + + if( isObject( %object ) ) + GuiFormManager::BroadcastContentMessage( %object, FormContentManager, "onLibraryDestroyed" ); + + FormContentManager.remove( %object ); + } + + // Destroy SimGroup. + if( isObject( FormContentManager ) ) + FormContentManager.delete(); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormMessageManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormMessageManager.ed.cs new file mode 100644 index 000000000..4fee6e8b7 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormMessageManager.ed.cs @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Send a Message to all instances of a Content. +// +// Returns : The Number of Objects Communicated With or (0 if None). +//----------------------------------------------------------------------------- +function GuiFormManager::SendContentMessage( %contentObj, %sender, %message ) +{ + // See if we Found the content object. + if( %contentObj == 0 || !isObject( %contentObj ) ) + { + //error( "GuiFormManager::SendContentMessage - Invalid Content Specified!" ); + return 0; + } + + // Validate Ref List. + if( !isObject( %contentObj.RefList ) ) + { + //error( "GuiFormManager::SendContentMessage - Unable to find content RefList!" ); + return 0; + } + + %refListObj = %contentObj.RefList.getID(); + + %messagedObjects = 0; + // Look for the content by name in our library. + for( %i = 0; %i < %refListObj.getCount(); %i++ ) + { + %object = %refListObj.getObject( %i ); + + // Check for alternate MessageControl + if( isObject( %object.MessageControl ) && %object.MessageControl.isMethod("onContentMessage") ) + %object.MessageControl.onContentMessage( %sender, %message ); + else if( %object.isMethod("onContentMessage") ) // Check for Default + %object.onContentMessage( %sender, %message ); + else + continue; + %messagedObjects++; + } + + // Return Success. + return %messagedObjects; +} + + + +//----------------------------------------------------------------------------- +// Send a Message to all instances of all Content. +// +// Returns : The Number of Objects Communicated With or (0 if None). +//----------------------------------------------------------------------------- +function GuiFormManager::BroadcastContentMessage( %libraryName, %sender, %message ) +{ + %libraryObj = GuiFormManager::FindLibrary( %libraryName ); + // See if we Found the content object. + if( %libraryObj == 0 || !isObject( %libraryObj ) ) + { + //error( "GuiFormManager::BroadcastContentMessage - Invalid Library Specified!" ); + return 0; + } + + // In a library the 0 object is always the ref group. + %contentRefGroup = %libraryObj.getObject( 0 ); + + // Validate Ref Group. + if( !isObject( %contentRefGroup ) ) + { + //error( "GuiFormManager::BroadcastContentMessage - Unable to find library RefGroup!" ); + return 0; + } + + // Clear messaged object count + %messagedObjects = 0; + + // Iterate over all contents ref lists and message everyone + for( %refGroupIter = 0; %refGroupIter < %contentRefGroup.getCount(); %refGroupIter++ ) + { + + // Fetch the Object Reference List Set + %refListSet = %contentRefGroup.getObject( %refGroupIter ); + + + // Look for the content by name in our library. + for( %i = 0; %i < %refListSet.getCount(); %i++ ) + { + %object = %refListSet.getObject( %i ); + + // Check for alternate MessageControl + if( isObject( %object.MessageControl ) && %object.MessageControl.isMethod("onContentMessage") ) + %object.MessageControl.onContentMessage( %sender, %message ); + else if( %object.isMethod("onContentMessage") ) // Check for Default + %object.onContentMessage( %sender, %message ); + else + continue; + + // Increment Messaged Object Count. + %messagedObjects++; + } + + } + + // Return Success. + return %messagedObjects; +} + diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormReferenceManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormReferenceManager.ed.cs new file mode 100644 index 000000000..811947bbb --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/guiFormReferenceManager.ed.cs @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Add Content Reference to RefList +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::AddContentReference( %library, %contentName, %control ) +{ + // Fetch Content Object. + %contentObj = GuiFormManager::FindFormContent( %library, %contentName ); + + // See if we Found the Library. + if( %contentObj == 0 || !isObject( %contentObj ) ) + { + error( "GuiFormManager::AddContentReference - Unable to Find Library by Name or ID!" ); + return false; + } + + // Validate Ref List. + if( !isObject( %contentObj.RefList ) ) + { + error( "GuiFormManager::AddContentReference - Unable to find content RefList!" ); + return false; + } + + //error("adding ref for object" SPC %control ); + + // Add Control Reference. + %contentObj.RefList.add( %control ); + + // Return Success. + return true; +} + +//----------------------------------------------------------------------------- +// Remove Content Reference from RefList +// +// Returns : True or False. +//----------------------------------------------------------------------------- +function GuiFormManager::RemoveContentReference( %library, %contentName, %control ) +{ + // Fetch Content Object. + %contentObj = GuiFormManager::FindFormContent( %library, %contentName ); + + // See if we Found the Library. + if( %contentObj == 0 || !isObject( %contentObj ) ) + { + error( "GuiFormManager::AddContentReference - Unable to Find Library by Name or ID!" ); + return false; + } + + // Validate Ref List. + if( !isObject( %contentObj.RefList ) ) + { + error( "GuiFormManager::AddContentReference - Unable to find content RefList!" ); + return false; + } + + //error("removing ref for object" SPC %control ); + + // Add Control Reference. + %contentObj.RefList.remove( %control ); + + if( %control.isMethod("onFormRemove") ) + %control.onFormRemove(); + + // Return Success. + return true; +} + +//----------------------------------------------------------------------------- +// Gets the current number of instances of the specified content that are active +// +// Returns : Number of instances or 0. +//----------------------------------------------------------------------------- +function GuiFormManager::GetContentCount( %library, %contentName ) +{ + // Fetch Content Object. + %contentObj = GuiFormManager::FindFormContent( %library, %contentName ); + + // See if we Found the Library. + if( %contentObj == 0 || !isObject( %contentObj ) ) + { + error( "GuiFormManager::GetContentCount - Unable to Find Library by Name or ID!" ); + return 0; + } + + // Validate Ref List. + if( !isObject( %contentObj.RefList ) ) + { + error( "GuiFormManager::GetContentCount - Unable to find content RefList!" ); + return 0; + } + + // Return Count. + return %contentObj.RefList.getCount(); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/input/applicationEvents.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/input/applicationEvents.ed.cs new file mode 100644 index 000000000..a4343f410 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/input/applicationEvents.ed.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +/// Public Application Events +/// +Input::GetEventManager().registerEvent( "ClosePressed" ); +Input::GetEventManager().registerEvent( "BeginShutdown" ); +Input::GetEventManager().registerEvent( "FocusChanged" ); + +function onClosePressed() +{ + //error("% Application Close - User Pressed the X button on their window"); + Input::GetEventManager().postEvent( "ClosePressed" ); +} + +function onPreExit() +{ + //error("% Application Close - quit called or quit message received""); + Input::GetEventManager().postEvent( "BeginShutdown" ); +} + +function onWindowFocusChange( %focused ) +{ + //error("% Application Close - quit called or quit message received""); + Input::GetEventManager().postEvent( "FocusChanged", %focused ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/input/dragDropEvents.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/input/dragDropEvents.ed.cs new file mode 100644 index 000000000..70582bd20 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/input/dragDropEvents.ed.cs @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +/// Public DragDrop Events +/// +Input::GetEventManager().registerEvent( "BeginDropFiles" ); +Input::GetEventManager().registerEvent( "DropFile" ); +Input::GetEventManager().registerEvent( "EndDropFiles" ); + +function onDropBegin( %fileCount ) +{ + //error("% DragDrop - Beginning file dropping of" SPC %fileCount SPC " files."); + Input::GetEventManager().postEvent( "BeginDropFiles", %fileCount ); +} +function onDropFile( %filePath ) +{ + //error(" % DragDrop - Got File : " SPC %filePath ); + Input::GetEventManager().postEvent( "DropFile", %filePath ); +} +function onDropEnd( %fileCount ) +{ + + //error("% DragDrop - Completed file dropping"); + Input::GetEventManager().postEvent( "EndDropFiles" ); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/input/inputEvents.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/input/inputEvents.ed.cs new file mode 100644 index 000000000..ba1b9e21a --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/input/inputEvents.ed.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +/// Returns Projects API's EventManager Singleton +/// +function Input::GetEventManager() +{ + if( !isObject( $_Tools::InputEventManager ) ) + $_Tools::InputEventManager = new EventManager() { queue = "InputEventManager"; }; + + return $_Tools::InputEventManager; +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/platform/.gitignore b/Templates/BaseGame/game/tools/editorClasses/scripts/platform/.gitignore new file mode 100644 index 000000000..1bc0e838a --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/platform/.gitignore @@ -0,0 +1 @@ +# Keep directory in git repo diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/preferencesManager.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/preferencesManager.ed.cs new file mode 100644 index 000000000..62f84b0d3 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/preferencesManager.ed.cs @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//*** Initializes the Preferences Manager +function initPreferencesManager() +{ + // FIXME TGEA doesnt currently have these due to the way it's built + return; + + //*** Create the Preferences Manager singleton + %pm = new PreferencesManager(pref); + registerPreferencesManager(%pm.getId()); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectEvents.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectEvents.ed.cs new file mode 100644 index 000000000..dc00a49a2 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectEvents.ed.cs @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +/// Returns Projects API's EventManager Singleton +/// +function Projects::GetEventManager() +{ + if( !isObject( $_Tools::ProjectEventManager ) ) + $_Tools::ProjectEventManager = new EventManager() { queue = "ProjectEventManager"; }; + + return $_Tools::ProjectEventManager; +} + + +function Projects::DeclareProjectTarget( %projectTargetNamespace, %objectGlobalName ) +{ + // At some point it would be nice to have a console method + // on SimObject that supported validating that another object + // implemented all the methods provided by a given namespace. + // .validateInterface("myNamespace") or some such. + %projectObject = new ScriptMsgListener( %objectGlobalName ) + { + class = %projectTargetNamespace; + superclass = ProjectBase; + }; +} + +/// +/// Public Project Events +/// + +/// ProjectOpened +/// +/// is fired when a project has been opened and all bootstrap +/// processing has occured on the project object. +/// At this point it is safe for addons to do post-load processing +/// such as creating new create entries and other specific modifications +/// to the editor. +Projects::GetEventManager().registerEvent( "ProjectOpened" ); + +/// ProjectClosed +/// +/// is fired when a project is about to be closed and it's +/// resources destroyed by the base project class. Addons +/// should use this event to free any project specific resources +/// they have allocated, as well as saving of data where applicable. +Projects::GetEventManager().registerEvent( "ProjectClosed" ); + +/// ProjectDeploy +/// +/// is fired when a game is about to be run from the editor and on +/// this event addons and third party's should without scheduling or +/// other delaying calls, deploy any game data that the game will need +/// to it's game path. +/// +/// Example, the core package zip code intercepts this message and +/// builds and deploys a new core.zip if is necessary +Projects::GetEventManager().registerEvent( "ProjectDeploy" ); + +/// Currently Unused +Projects::GetEventManager().registerEvent( "ProjectFileAdded" ); +/// Currently Unused +Projects::GetEventManager().registerEvent( "ProjectFileRemoved" ); + +/// +/// ProjectOpen Event Handler +/// - %data is the project object to be opened +function ProjectBase::onProjectOpen( %this, %data ) +{ + error("onProjectOpen Handler not implemented for class -" SPC %this.class ); +} + +/// +/// ProjectClose Event Handler +/// +function ProjectBase::onProjectClose( %this, %data ) +{ + error("onProjectClose Handler not implemented for class -" SPC %this.class ); +} + +/// +/// ProjectAddFile Event Handler +/// +function ProjectBase::onProjectAddFile( %this, %data ) +{ + error("onProjectAddFile Handler not implemented for class -" SPC %this.class ); +} + +/// +/// ProjectRemoveFile Event Handler +/// +function ProjectBase::onProjectRemoveFile( %this, %data ) +{ + error("onProjectRemoveFile Handler not implemented for class -" SPC %this.class ); +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectInternalInterface.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectInternalInterface.ed.cs new file mode 100644 index 000000000..398eff6d9 --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/projects/projectInternalInterface.ed.cs @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// +/// Internal Project Events +/// +Projects::GetEventManager().registerEvent( "_ProjectCreate" ); +Projects::GetEventManager().registerEvent( "_ProjectOpen" ); +Projects::GetEventManager().registerEvent( "_ProjectClose" ); +Projects::GetEventManager().registerEvent( "_ProjectAddFile" ); +Projects::GetEventManager().registerEvent( "_ProjectRemoveFile" ); + +/// +/// Project Context Methods +/// + +function ProjectBase::isActive( %this ) +{ + if( Projects::GetEventManager().activeProject == %this.getId() ) + return true; + else + return false; +} + +function ProjectBase::getActiveProject( %this ) +{ + return Projects::GetEventManager().activeProject; +} + +function ProjectBase::setActive( %this ) +{ + %activeProject = %this.getActiveProject(); + + if( isObject( %activeProject ) ) + { + // If another is active, properly post a close event for now THEN + // and only then should we change the .activeProject field on the evtmgr + } + + Projects::GetEventManager().activeProject = %this; +} + +function ProjectBase::onAdd( %this ) +{ + // Subscribe to base events + Projects::GetEventManager().subscribe( %this, "_ProjectCreate", "_onProjectCreate" ); + Projects::GetEventManager().subscribe( %this, "_ProjectOpen", "_onProjectOpen" ); + Projects::GetEventManager().subscribe( %this, "_ProjectClose", "_onProjectClose" ); + Projects::GetEventManager().subscribe( %this, "_ProjectAddFile", "_onProjectAddFile" ); + Projects::GetEventManager().subscribe( %this, "_ProjectRemoveFile", "_onProjectRemoveFile" ); + +} + +function ProjectBase::onRemove( %this ) +{ + // Remove subscriptions to base events + Projects::GetEventManager().remove( %this, "_ProjectCreate" ); + Projects::GetEventManager().remove( %this, "_ProjectOpen" ); + Projects::GetEventManager().remove( %this, "_ProjectClose" ); + Projects::GetEventManager().remove( %this, "_ProjectAddFile" ); + Projects::GetEventManager().remove( %this, "_ProjectRemoveFile" ); + +} + +/// +/// Internal ProjectOpen Event Handler +/// - %data is the project file path to be opened +function ProjectBase::_onProjectOpen( %this, %data ) +{ + // Sanity check calling of this + if( !%this.isMethod( "onProjectOpen" ) ) + { + error("Incomplete Project Interface - onProjectOpen method is non-existent!"); + return false; + } + + if( !%this.LoadProject( %data ) ) + { + messageBox("Unable to Load Project", "The project file you're attempting to open was created with an incompatible version of this software\n\nConversion of 1.1.X projects will be addressed soon, we apologize for the inconvenience.","Ok","Error"); + + return false; + } + + %this.gamePath = filePath( %data ); + %this.projectFile = %data; + + %toggle = $Scripts::ignoreDSOs; + $Scripts::ignoreDSOs = true; + + %this.gameResPath = %this.gamePath @ "/*"; + + // Set current dir to game + setCurrentDirectory( %this.gamePath ); + + // Set ^game expando + setScriptPathExpando("project", %this.gamePath ); + setScriptPathExpando("game", %this.gamePath @ "/game" ); + + %this.onProjectOpen( %data ); + %this.setActive(); + + Projects::GetEventManager().postEvent( "ProjectOpened", %this ); + + $Scripts::ignoreDSOs = %toggle; + $pref::lastProject = %data; +} + +/// +/// Internal ProjectClose Event Handler +/// +function ProjectBase::_onProjectClose( %this, %data ) +{ + + Projects::GetEventManager().postEvent( "ProjectClosed", %this ); + + // Sanity check calling of this + if( !%this.isMethod( "onProjectClose" ) ) + error("Incomplete Project Interface - onProjectClose method is non-existent!"); + else + %this.onProjectClose( %data ); + + // Reset to tools directory + setCurrentDirectory( getMainDotCsDir() ); + + // Remove expandos + removeScriptPathExpando( "game" ); + removeScriptPathExpando( "project" ); +} + +/// +/// Internal ProjectCreate Event Handler (Optionally Inherited by public interface) +/// +function ProjectBase::_onProjectCreate( %this, %data ) +{ + // Force a write out of the project file + if( !%this.SaveProject( %data ) ) + return false; + + // Sanity check calling of this + if( %this.isMethod( "onProjectCreate" ) ) + %this.onProjectCreate( %data ); +} + + +/// +/// Internal ProjectAddFile Event Handler +/// +function ProjectBase::_onProjectAddFile( %this, %data ) +{ + // Sanity check calling of this + if( !%this.isMethod( "onProjectAddFile" ) ) + error("Incomplete Project Interface - onProjectAddFile method is non-existent!"); + else + %this.onProjectAddFile( %data ); + +} + +/// +/// Internal ProjectRemoveFile Event Handler +/// +function ProjectBase::_onProjectRemoveFile( %this, %data ) +{ + // Sanity check calling of this + if( !%this.isMethod( "onProjectRemoveFile" ) ) + error("Incomplete Project Interface - onProjectRemoveFile method is non-existent!"); + else + %this.onProjectRemoveFile( %data ); + +} diff --git a/Templates/BaseGame/game/tools/editorClasses/scripts/utility.ed.cs b/Templates/BaseGame/game/tools/editorClasses/scripts/utility.ed.cs new file mode 100644 index 000000000..e1dadfeee --- /dev/null +++ b/Templates/BaseGame/game/tools/editorClasses/scripts/utility.ed.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function isInList( %word, %list ) +{ + %count = getWordCount( %list ); + for( %i = 0; %i < %count; %i++ ) + { + %entry = getWord( %list, %i ); + if( %word $= %entry ) + return true; + } + + return false; +} + +function isInFieldList(%word, %list) +{ + %count = getFieldCount( %list ); + for( %i = 0; %i < %count; %i++ ) + { + %entry = getField( %list, %i ); + if( %word $= %entry ) + return true; + } + + return false; +} diff --git a/Templates/BaseGame/game/tools/forestEditor/brushes.cs b/Templates/BaseGame/game/tools/forestEditor/brushes.cs new file mode 100644 index 000000000..e99d2e537 --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/brushes.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + diff --git a/Templates/BaseGame/game/tools/forestEditor/forestEditToolbar.ed.gui b/Templates/BaseGame/game/tools/forestEditor/forestEditToolbar.ed.gui new file mode 100644 index 000000000..782383121 --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/forestEditToolbar.ed.gui @@ -0,0 +1,437 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ForestEditToolbar,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ForestEditToolbar"; + canSaveDynamicFields = "1"; + enabled = "1"; + + new GuiTextCtrl() { + text = "Brush Settings"; + maxLength = "255"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "6 7"; + Extent = "70 16"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "760 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiControl(ForestBrushSizeTextEditContainer) { + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Size"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "21 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "9"; + maxLength = "4"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 18"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + AltCommand = "ForestTools->BrushTool.size = $ThisControl.getValue();"; + validate = "ForestEditorGui.validateBrushSize();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "textEdit"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/dropslider"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(ForestBrushSizeSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes size of the brush"; + hovertime = "750"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "200 3"; + Extent = "2 26"; + MinExtent = "1 1"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl(ForestBrushPressureTextEditContainer) { + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "208 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Pressure"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "100"; + maxLength = "3"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 18"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ForestTools->BrushTool.pressure = $ThisControl.getValue() / 100;"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "textEdit"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/dropslider"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(ForestBrushPressureSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the pressure"; + hovertime = "750"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "336 3"; + Extent = "2 26"; + MinExtent = "1 1"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl(ForestBrushHardnessTextEditContainer) { + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "352 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Hardness"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "1"; + maxLength = "3"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 18"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ForestTools->BrushTool.hardness = $ThisControl.getValue() / 100);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "textEdit"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/dropslider"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(ForestBrushHardnessSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the hardness curve."; + hovertime = "750"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; + +new GuiMouseEventCtrl(ForestBrushSizeSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(ForestBrushSizeTextEditContainer.position) + firstWord(ForestEditToolbar.position)+11 SPC + (getWord(ForestBrushSizeTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ForestTools->BrushTool.size = $ThisControl.value;"; + range = "1" SPC getWord(ETerrainEditor.maxBrushSize, 0); + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(ForestBrushPressureSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(ForestBrushPressureTextEditContainer.position) + firstWord(ForestEditToolbar.position) SPC + (getWord(ForestBrushPressureTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ForestTools->BrushTool.pressure = $ThisControl.value;"; + range = "0.01 1"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(ForestBrushHardnessSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(ForestBrushHardnessTextEditContainer.position) + firstWord(ForestEditToolbar.position) SPC + (getWord(TForestBrushHardnessTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ForestTools->BrushTool.hardness = $ThisControl.value;"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/forestEditor/forestEditor.cs b/Templates/BaseGame/game/tools/forestEditor/forestEditor.cs new file mode 100644 index 000000000..274f735a5 --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/forestEditor.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile (ForestEditorProfile) +{ + canKeyFocus = true; + category = "Editor"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.cs b/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.cs new file mode 100644 index 000000000..e0e9f1ce4 --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.cs @@ -0,0 +1,502 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// ForestEditorGui Script Methods + +function ForestEditorGui::setActiveTool( %this, %tool ) +{ + if ( %tool == ForestTools->BrushTool ) + ForestEditTabBook.selectPage(0); + + Parent::setActiveTool( %this, %tool ); +} + +/// This is called by the editor when the active forest has +/// changed giving us a chance to update the GUI. +function ForestEditorGui::onActiveForestUpdated( %this, %forest, %createNew ) +{ + %gotForest = isObject( %forest ); + + // Give the user a chance to add a forest. + if ( !%gotForest && %createNew ) + { + MessageBoxYesNo( "Forest", + "There is not a Forest in this mission. Do you want to add one?", + %this @ ".createForest();", "" ); + return; + } +} + +/// Called from a message box when a forest is not found. +function ForestEditorGui::createForest( %this ) +{ + %forestObject = parseMissionGroupForIds("Forest", ""); + + if ( isObject( %forestObject ) ) + { + error( "Cannot create a second 'theForest' Forest!" ); + return; + } + + // Allocate the Forest and make it undoable. + new Forest( theForest ) + { + dataFile = ""; + parentGroup = "MissionGroup"; + }; + + MECreateUndoAction::submit( theForest ); + + ForestEditorGui.setActiveForest( theForest ); + + //Re-initialize the editor settings so we can start using it immediately. + %tool = ForestEditorGui.getActiveTool(); + if ( isObject( %tool ) ) + %tool.onActivated(); + + if ( %tool == ForestTools->SelectionTool ) + { + %mode = GlobalGizmoProfile.mode; + switch$ (%mode) + { + case "None": + ForestEditorSelectModeBtn.performClick(); + case "Move": + ForestEditorMoveModeBtn.performClick(); + case "Rotate": + ForestEditorRotateModeBtn.performClick(); + case "Scale": + ForestEditorScaleModeBtn.performClick(); + } + } + else if ( %tool == ForestTools->BrushTool ) + { + %mode = ForestTools->BrushTool.mode; + switch$ (%mode) + { + case "Paint": + ForestEditorPaintModeBtn.performClick(); + case "Erase": + ForestEditorEraseModeBtn.performClick(); + case "EraseSelected": + ForestEditorEraseSelectedModeBtn.performClick(); + } + } + + EWorldEditor.isDirty = true; +} + +function ForestEditorGui::newBrush( %this ) +{ + %internalName = getUniqueInternalName( "Brush", ForestBrushGroup, true ); + + %brush = new ForestBrush() + { + internalName = %internalName; + parentGroup = ForestBrushGroup; + }; + + MECreateUndoAction::submit( %brush ); + + ForestEditBrushTree.open( ForestBrushGroup ); + ForestEditBrushTree.buildVisibleTree(true); + %item = ForestEditBrushTree.findItemByObjectId( %brush ); + ForestEditBrushTree.clearSelection(); + ForestEditBrushTree.addSelection( %item ); + ForestEditBrushTree.scrollVisible( %item ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditorGui::newElement( %this ) +{ + %sel = ForestEditBrushTree.getSelectedObject(); + + if ( !isObject( %sel ) ) + %parentGroup = ForestBrushGroup; + else + { + if ( %sel.getClassName() $= "ForestBrushElement" ) + %parentGroup = %sel.parentGroup; + else + %parentGroup = %sel; + } + + %internalName = getUniqueInternalName( "Element", ForestBrushGroup, true ); + + %element = new ForestBrushElement() + { + internalName = %internalName; + parentGroup = %parentGroup; + }; + + MECreateUndoAction::submit( %element ); + + ForestEditBrushTree.clearSelection(); + ForestEditBrushTree.buildVisibleTree( true ); + %item = ForestEditBrushTree.findItemByObjectId( %element.getId() ); + ForestEditBrushTree.scrollVisible( %item ); + ForestEditBrushTree.addSelection( %item ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditorGui::deleteBrushOrElement( %this ) +{ + ForestEditBrushTree.deleteSelection(); + ForestEditorPlugin.dirty = true; +} + +function ForestEditorGui::newMesh( %this ) +{ + %spec = "All Mesh Files|*.dts;*.dae|DTS|*.dts|DAE|*.dae"; + + %dlg = new OpenFileDialog() + { + Filters = %spec; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = true; + }; + + %ret = %dlg.Execute(); + + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %fullPath = makeRelativePath( %dlg.FileName, getMainDotCSDir() ); + %file = fileBase( %fullPath ); + } + + %dlg.delete(); + + if ( !%ret ) + return; + + %name = getUniqueName( %file ); + + %str = "datablock TSForestItemData( " @ %name @ " ) { shapeFile = \"" @ %fullPath @ "\"; };"; + eval( %str ); + + if ( isObject( %name ) ) + { + ForestEditMeshTree.clearSelection(); + ForestEditMeshTree.buildVisibleTree( true ); + %item = ForestEditMeshTree.findItemByObjectId( %name.getId() ); + ForestEditMeshTree.scrollVisible( %item ); + ForestEditMeshTree.addSelection( %item ); + + ForestDataManager.setDirty( %name, "art/forest/managedItemData.cs" ); + + %element = new ForestBrushElement() + { + internalName = %name; + forestItemData = %name; + parentGroup = ForestBrushGroup; + }; + + ForestEditBrushTree.clearSelection(); + ForestEditBrushTree.buildVisibleTree( true ); + %item = ForestEditBrushTree.findItemByObjectId( %element.getId() ); + ForestEditBrushTree.scrollVisible( %item ); + ForestEditBrushTree.addSelection( %item ); + + pushInstantGroup(); + %action = new MECreateUndoAction() + { + actionName = "Create TSForestItemData"; + }; + popInstantGroup(); + + %action.addObject( %name ); + %action.addObject( %element ); + %action.addToManager( Editor.getUndoManager() ); + + ForestEditorPlugin.dirty = true; + } +} + +function ForestEditorGui::deleteMesh( %this ) +{ + %obj = ForestEditMeshTree.getSelectedObject(); + + // Can't delete itemData's that are in use without + // crashing at the moment... + + if ( isObject( %obj ) ) + { + MessageBoxOKCancel( "Warning", + "Deleting this mesh will also delete BrushesElements and ForestItems referencing it.", + "ForestEditorGui.okDeleteMesh(" @ %obj @ ");", + "" ); + } +} + +function ForestEditorGui::okDeleteMesh( %this, %mesh ) +{ + // Remove mesh from file + ForestDataManager.removeObjectFromFile( %mesh, "art/forest/managedItemData.cs" ); + + // Submitting undo actions is handled in code. + %this.deleteMeshSafe( %mesh ); + + // Update TreeViews. + ForestEditBrushTree.buildVisibleTree( true ); + ForestEditMeshTree.buildVisibleTree( true ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditorGui::validateBrushSize( %this ) +{ + %minBrushSize = 1; + %maxBrushSize = getWord(ETerrainEditor.maxBrushSize, 0); + + %val = $ThisControl.getText(); + if(%val < %minBrushSize) + $ThisControl.setValue(%minBrushSize); + else if(%val > %maxBrushSize) + $ThisControl.setValue(%maxBrushSize); +} + + + +// Child-control Script Methods + + +function ForestEditMeshTree::onSelect( %this, %obj ) +{ + ForestEditorInspector.inspect( %obj ); +} + +function ForestEditBrushTree::onRemoveSelection( %this, %obj ) +{ + %this.buildVisibleTree( true ); + ForestTools->BrushTool.collectElements(); + + if ( %this.getSelectedItemsCount() == 1 ) + ForestEditorInspector.inspect( %obj ); + else + ForestEditorInspector.inspect( "" ); +} + +function ForestEditBrushTree::onAddSelection( %this, %obj ) +{ + %this.buildVisibleTree( true ); + ForestTools->BrushTool.collectElements(); + + if ( %this.getSelectedItemsCount() == 1 ) + ForestEditorInspector.inspect( %obj ); + else + ForestEditorInspector.inspect( "" ); +} + +function ForestEditTabBook::onTabSelected( %this, %text, %idx ) +{ + %bbg = ForestEditorPalleteWindow.findObjectByInternalName("BrushButtonGroup"); + %mbg = ForestEditorPalleteWindow.findObjectByInternalName("MeshButtonGroup"); + + %bbg.setVisible( false ); + %mbg.setVisible( false ); + + if ( %text $= "Brushes" ) + { + %bbg.setVisible( true ); + %obj = ForestEditBrushTree.getSelectedObject(); + ForestEditorInspector.inspect( %obj ); + } + else if ( %text $= "Meshes" ) + { + %mbg.setVisible( true ); + %obj = ForestEditMeshTree.getSelectedObject(); + ForestEditorInspector.inspect( %obj ); + } +} + +function ForestEditBrushTree::onDeleteSelection( %this ) +{ + %list = ForestEditBrushTree.getSelectedObjectList(); + + MEDeleteUndoAction::submit( %list, true ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditBrushTree::onDragDropped( %this ) +{ + ForestEditorPlugin.dirty = true; +} + +function ForestEditMeshTree::onDragDropped( %this ) +{ + ForestEditorPlugin.dirty = true; +} + +function ForestEditMeshTree::onDeleteObject( %this, %obj ) +{ + // return true - skip delete. + return true; +} + +function ForestEditMeshTree::onDoubleClick( %this ) +{ + %obj = %this.getSelectedObject(); + + %name = getUniqueInternalName( %obj.getName(), ForestBrushGroup, true ); + + %element = new ForestBrushElement() + { + internalName = %name; + forestItemData = %obj.getName(); + parentGroup = ForestBrushGroup; + }; + + //ForestDataManager.setDirty( %element, "art/forest/brushes.cs" ); + + ForestEditBrushTree.clearSelection(); + ForestEditBrushTree.buildVisibleTree( true ); + %item = ForestEditBrushTree.findItemByObjectId( %element ); + ForestEditBrushTree.scrollVisible( %item ); + ForestEditBrushTree.addSelection( %item ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditBrushTree::handleRenameObject( %this, %name, %obj ) +{ + if ( %name !$= "" ) + { + %found = ForestBrushGroup.findObjectByInternalName( %name ); + if ( isObject( %found ) && %found.getId() != %obj.getId() ) + { + MessageBoxOK( "Error", "Brush or Element with that name already exists.", "" ); + + // true as in, we handled it, don't rename the object. + return true; + } + } + + // Since we aren't showing any groups whens inspecting a ForestBrushGroup + // we can't push this event off to the inspector to handle. + + //return GuiTreeViewCtrl::handleRenameObject( %this, %name, %obj ); + + + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + %nameOrClass = %obj.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %obj.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ "internalName" @ " Change"; + + objectId = %obj.getId(); + fieldName = "internalName"; + fieldValue = %obj.internalName; + arrayIndex = 0; + + inspectorGui = ""; + }; + + // Restore the instant group. + popInstantGroup(); + + %action.addToManager( Editor.getUndoManager() ); + EWorldEditor.isDirty = true; + + return false; +} + +function ForestEditorInspector::inspect( %this, %obj ) +{ + if ( isObject( %obj ) ) + %class = %obj.getClassName(); + + %this.showObjectName = false; + %this.showCustomFields = false; + + switch$ ( %class ) + { + case "ForestBrush": + %this.groupFilters = "+NOTHING,-Ungrouped"; + + case "ForestBrushElement": + %this.groupFilters = "+ForestBrushElement,-Ungrouped"; + + case "TSForestItemData": + %this.groupFilters = "+Media,+Wind"; + + default: + %this.groupFilters = ""; + } + + Parent::inspect( %this, %obj ); +} + +function ForestEditorInspector::onInspectorFieldModified( %this, %object, %fieldName, %oldValue, %newValue ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + %instantGroup = $InstantGroup; + $InstantGroup = 0; + + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %oldValue; + + inspectorGui = %this; + }; + + // Restore the instant group. + $InstantGroup = %instantGroup; + + %action.addToManager( Editor.getUndoManager() ); + + if ( %object.getClassName() $= "TSForestItemData" ) + ForestDataManager.setDirty( %object ); + + ForestEditorPlugin.dirty = true; +} + +function ForestEditorInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + //FieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +function ForestBrushSizeSliderCtrlContainer::onWake(%this) +{ + %this-->slider.range = "1" SPC getWord(ETerrainEditor.maxBrushSize, 0); + %this-->slider.setValue(ForestBrushSizeTextEditContainer-->textEdit.getValue()); +} diff --git a/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.gui b/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.gui new file mode 100644 index 000000000..e46a09f3d --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/forestEditorGui.gui @@ -0,0 +1,511 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new ForestEditorCtrl(ForestEditorGui,EditorGuiGroup) { + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "255 0 0 120"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + cameraZRot = "0"; + forceFOV = "0"; + reflectPriority = "1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ForestEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiWindowCollapseCtrl(ForestEditorPalleteWindow) { + CollapseGroup = "-1"; + CollapseGroupNum = "-1"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Forest Editor"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowCollapseProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "210 252"; + MinExtent = "210 100"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "PalleteWindow"; + canSaveDynamicFields = "0"; + + new GuiTabBookCtrl(ForestEditTabBook) { + TabPosition = "Top"; + TabMargin = "10"; + MinTabWidth = "60"; + TabHeight = "20"; + AllowReorder = "0"; + FrontTabPadding = "0"; + Docking = "Client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "3 44"; + Extent = "210 205"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl() { + fitBook = "0"; + text = "Brushes"; + maxLength = "1024"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 22"; + Extent = "210 194"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "BrushesTab"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "210 194"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl(ForestEditBrushTree) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + ClearAllOnSingleSelection = "1"; + showRoot = "0"; + internalNamesOnly = "1"; + objectNamesOnly = "0"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "208 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiTabPageCtrl() { + fitBook = "0"; + text = "Meshes"; + maxLength = "1024"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 22"; + Extent = "210 183"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "MeshesTab"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "210 194"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl(ForestEditMeshTree) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "0"; + DragToItemAllowed = "0"; + ClearAllOnSingleSelection = "1"; + showRoot = "0"; + internalNamesOnly = "0"; + objectNamesOnly = "1"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "0"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 -67"; + Extent = "208 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "$ThisControl.onDoubleClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiStackControl() { + StackingType = "Horizontal"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "3"; + DynamicSize = "1"; + ChangeChildSizeToFit = "0"; + ChangeChildPosition = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "170 25"; + Extent = "35 17"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "MeshButtonGroup"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/forestEditor/images/new-mesh"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.newMesh();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add New Mesh"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "19 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.deleteMesh();"; + tooltip = "Delete Selected"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiStackControl() { + StackingType = "Horizontal"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "3"; + DynamicSize = "1"; + ChangeChildSizeToFit = "0"; + ChangeChildPosition = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "151 25"; + Extent = "54 17"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "BrushButtonGroup"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/forestEditor/images/new-brush"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.newBrush();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add New Brush Group"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/forestEditor/images/new-element"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "19 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.newElement();"; + tooltip = "Add New Brush Element"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "38 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.deleteBrushOrElement();"; + tooltip = "Delete Selected"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiWindowCollapseCtrl(ForestEditorPropertiesWindow) { + CollapseGroup = "-1"; + CollapseGroupNum = "-1"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowCollapseProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(ForestEditorPalleteWindow.extent, 1) - 2; + Extent = "210 460"; + MinExtent = "210 50"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "PropertiesWindow"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Docking = "Client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 23"; + Extent = "210 263"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiInspector(ForestEditorInspector) { + dividerMargin = "5"; + showCustomFields = "0"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + DynamicSize = "1"; + ChangeChildSizeToFit = "1"; + ChangeChildPosition = "1"; + isContainer = "1"; + Profile = "GuiInspectorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "1 1"; + Extent = "193 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Inspector"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_d.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..24af47fb0ef7eb984ed6f85fd2d04d13565d86c0 GIT binary patch literal 751 zcmV<L0ucR)P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!kx4{BRCwC7mCsKTQ4q(!rMuk)+Daid zYz(**4<rz!2}TWIqQ=BfL<=Y4!D77g-*9cu93V02%|yKj(VA|;54Uijw2f_AN$Gaw zM@v}eQ6v%R4@;S3vYEHL@00n?do!lFxw%0Ai{vsIWKAX$IG%^aVu2u7qJkiLIR<e$ zopOJFKWsJ|O!QGBEtAQ}sZ?^2fleT@EE8R5I7pHtWV2bhi3Kc^tAH?Ldol^RKSAA) z?HTO<2q63wXl(-oE0D_r_g))kpP0?&66hlHcs&2C1%tuAVuK)ii2#%k&~gpnTY#(v zMAiv(?%%SY^P8SG2Kf~)Wzs^K@U11t#`jW;T0xcUI64D87Qjjva7YxB>boqHQ^?KR zzRQD-w`QQ7LfV|b5tTxtnnYBp5y>YL<o<L2<v_}}k7)n-4qNLbNRiewCYO4e$sHda zO!zOp#I-^ai3E7@I)bZjAhwGr8YQ_@l1sExCIlIN;V<7UYJsFuDQs?TLf3Wl4m`!D z?N`X`3TKcWw+|yP{gvBNHApg<grX=AMG>m1;^5!_&aPp6h)e;^R=5Uz7_9=STBFRu z0^aZJU|?tnp->3jUENq)TZP?b$Bo{*XzO@{hm&t>lD;fxZ+;#}%gg9AbNE~+z}4eI zSP7%u;Xo`NgWK(fcXG1kgH#rjU0Q-pfQ~{Qjuj0`E(fVoLS!R?k<rl$J4aoheXA9D zst<$N$(!jMe~HK886W>|NX5g@L9%FvhvWt1$*puNC;Iy8f>Z=Ks0Q-%G!8iqda;08 zPafmh>}=iRf+j9s_N8lV?8F-~-f1XB*<CRV16N_`becV)oG1!jYA=gywzEliG7U+t h22R^3x%amK0|2X|=l+4vdD#E}002ovPDHLkV1khsPy+w} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_h.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..66d75bb5b8b7324563999a7442e42cd2b410881c GIT binary patch literal 1012 zcmV<Q0}K3#P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#mPtfGRCwC7mRnB~K@`W&?zY<&gl-X0 zilj6kVoXZG3m7j^U%m8&2i|=)F@)$N;n^<&310x-GzKG=*aTLnqF8Dup$!(;?iSdK z=L}tJmo~rylbmd4I&*&K{Ljqna<j9uR{&rTO@i}jIkYjCX_|wfP)KTQY&;iAHk*}_ z$>bn!S(en$&;a;67c2UfC?g=_IPT2RE`At_W!4v>KN7p(s};cQ<sq-?@ao;?QF{)- z$Ug%ahEWC@7#J9FI-Qu97%lC~ABSVv^g?vyy8@mX0R)kQ-MkK|q{{pq1JR%pG(J8) z>I9W8Ui$PFA&Z!a0^V94TnJGJvZk^A&d!eDa=9!)5SY>0iWyPUb)Ca;j-K`yEnb}; zd6`{b>|Xtuhq{0ZOt6X&6+9^4#pWA}nM_9Xcs!=x?>`Vl(W)w)oq?h#0>SLD&87Fx z{%$Tsej((7-vx$gL0&6nQo}=_>AHOP>F^Y;Bfq-W>*X+`L+LPLioQ}t;C8zWuJ}h{ zDgHuDCnKp<g+X*&tf~g2QYu0OkeWIXGBio+c%HZHlGy%vv!qjMR7Z@)L<x*Sq42yl z)Y<(z*9YD@FG3FGT0^-+9}m){YYak>(>jdJq9Exv`632y6s4<Xs;b5|Ha4Ot2i%O_ zhqQJFb`&^*_z`mZ$(u=<iy(`fqhvbP#=y)><_8(PQ6(cXP19m4D=Sf42+PaMfSEz4 zy$AkmM?moSz~9&*-+J_#$z?-O0i6stj$!*|yRKS|ax=5BZv`zH>FtHNxjATUZ3S7D zp}xKz!krP=$~MV84<55js3s&AWkO~{>~lo^*{*Yf4(8`$W+oH$Vg}RG(-01aAs&xI zAP_*w)(9r|-FtWhA%7?p%LMCcoch43o14-6-X7E-h}+(VI9hXaGb9oT2nK_4TU%Qh zgaCFX#SKvgV$#{5(v~-jTwc>8f_V_8=W;NUN<mjwmy8nrm#d^E&P<$VlCQMW_^VeZ zcQp+ZG6ac*J!td!<d&8e2Eiy<O~jJIn<wmEzS4x+_V#6WFgT^|@9(LYmDmuF!~Okb z=ftkbX&_}aa<<F8y<dDC9ba*a(;Ti+;Vadonsi5ZowHHY<OrRUyu3Z;amaO=W?!ng zt*tEyn{&5N<jfhe1blMTrz?rB7yAu*;s*n6Bx1O5@>n{Z9z;9#af1JubmGy-F-OwT i{ddN<G5a|AC%^!jl4|VR&YQFV0000<MNUMnLSTY79@ohL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_n.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-all-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ec82f5ac0599227fca72f74f15e6848531f1a46f GIT binary patch literal 671 zcmV;Q0$}}#P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!K}keGRCwCFmOHEBP!Pu_zGW9d8!cBW zt^GElZoa~W`w48d-EFoARtr{F1ku9UR(uztiwZtaWW`4kO}ujsCrj2FuZe{NL!y}T zJOBC5oXA}+7kEb4;7Rc`ki+Q9NF-wF;dDAJoX=-|Hk*OV<pR6i4w586tybgozZ(vR z#-La%_QAMbug6_OPNx$;pU=VRbb`%h15p%VwOW~1biL?xq*5tCmgNtS^?FS|W4JL@ zOod&IK|25JS3auAWbzk6zAu+c@OV5xx>gUtQvSx@eZ@Ru9?XD<Dkp={NF)+J5t7s( zs;W{?GzgZm&}y|_nWF!i?X#{UKAkXSl9^2A2QHjkEEeE)yFpQuf1WrT4x)rWUUbiD z3HKDKn(Sh+D4-9i@pw#?5Qtt0fwbFgJw#!fR87xOrBV@a*Zgj`gLpg+KA(?5^hyjM zBBsRd?}N(avVb$d6BYe_pOz{bje^(frE^0doleIHA~HpnyP#|~E7a?CJ{SxF?ik2* zFc^@<DJ&2O6uRB6DG2Q>qeHb+DhXH~o|K395e|o;*=&N}?}zDhO1lfgG=!WChKjI6 z{NZo_j^kjv-9n?$po&JL5%I+m<kPO(@ArQY_U(8)(t+G;HZYk?AQTD}^7*`F$W4z> z%?rW^klX_+@57Lrpj<BZhVA@{kiSF~`c<e_s}DnNHUoQ2kmKhIrboA-hafkbfrU2R zXWemP8Di!Ly0Gcipd4FVvI;Q+(iF%Hq`hM-|D$;qU;qo)l;~2Y#=QUl002ovPDHLk FV1nmWFChQ` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_d.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..8f45d199ea430af7d69bffe932c4370b8d82dd59 GIT binary patch literal 653 zcmV;80&@L{P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!FG)l}RCwC7mCtL_U>L{0P2V>0M<K4Z zHFV&+DIToST-@|xYw=c)^(u&~|G>I~cRhHYmtH4cyqPjA-ds-;#KS~TRIr&=Gg`Jp zhMhWX%$r%(H0ux6_`t)<`{d>O$@AvT`}GxzMHzr%C51=9`FtRO0D{3Fcs{7|ypSUd z5{*V_B9TBS6oSuM<YhD(4Z6OrDGX#oD5Vyi_Z%ci5_Das9GkFC{s)8w(_~x7wI^Oc zUayt#sFHWp{eC}4cQKpIx)zqpWmoOvW(8mMRp?u5__DEz548$fzkpj;m3?1=3?tf( z>bDA3KHbL~tz?U~c3_Vt%ip_seqv@gP_KAeoyW@Og<WAL`e#qP^=~|NBHjDQ4+D9- zQNr`uf>U^B57e&3$K=sKteegbI=kr1`0?byLB>xNdQ-K9yT!Zb@yG}r>D#J9>}+fX zli{=rHg<$!JlY#H_%N6N4-uCp=Rl99t?iL<@9~7Iv#G?*{`cR&D<mZ2_T?%fri|}f z29{pVquDeZ+ZGSY$XqCl1`>#fC(rI;`-g!@gyO~f2hQ(eowUOBWgHU3zB&W9?%`4% zXHKSYF*%FH<s1Y~gcOr;>@dY?DTV2CvqSxdWDsOLmb4)(@>i6DdsIKjrlBG;UGT;X zdp&sdu7n%c7Co~IE?`;ievz4)Ls+EVfh<!}HBEzQnohe&-SGrauh-e25<%c|wOUPK n-p-tmB_YI1w^gmo{S{yUxvu)ctMuGK00000NkvXXu0mjf$dfp4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_h.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..26189746f20aab0c5df20da97fa9569b22b65778 GIT binary patch literal 945 zcmV;i15W&jP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Q%OWYRCwC7mS0FyQ5?tb?%j4>({{WL z*+Sb;h>_$&LP;1}5n>QSkQyZQ)Kk6mB=iT79(oL4g6Ki0AZJSw=_#Tf#L__oHn9|8 zn1A-i?K)-NwcUF=zuV5d-rbu)dEmD*?w{}He1E_5JDVvS4!bCd3gAH0{tO<+uvwC% zfXCxOPN#EUD9L0JMWfLGEz2_EIF7>ezC=+Q(by67KjO{%C%y-ddi_TCA$61}ij>J@ zqI!CI`nA?Wq0oT#+NZCtz0sA3E5*kgiS;E{ED`mJd71K`yBP#6^#E522yPHiFc>To zB`=1*je9>vMo+H9zWc-iepv~;S`3gxoF*LS+pf*!ayb)-o6Tk!G~qjF>~dr@p1teq zoL`!1RRAC={T7zQ9Y_tTi*9aiGFVR$ve|4Rz)Ftx0!>6-_!fRlw-P}`ys!fxu!9F` z6L^PN;T?y=ktdoZP@zzuAdEnhvm>37%7M}Bu)C96TU!h`!NTA~23-KbY?d(D2(*%# zL)$_INqazGb|P;ARu1)tAhlH-L5;*eq;4&>h}43d{qVdXWGm7eYxQclm9b~)-NzD2 zL9jL{2LXv>*BLp2do*Pc8r&_@)#k=!r=xLwY;xebtf|i-*U<>kz#);el|Z0FYNQwS zT=Ux1vx9?dt~Q=dC2bQkLuc|obFA8pjYWx;CjaN618}2+R!cb<j6gOk5?+lzY|myh zELY>+h@}^r)OD$_IO1KWZ;e#2RXK10Zg9vXOC-t~Ry&4*ef~O^FWPn@FcW-x=MpWO zg-SaYXPHW#tMV*2AGt6KeWRp?re%g<Bs@#%)1lO`1f(5LL%01k)pfCsQ@vmE`8?g- z-v6>#HxurD1_G|bHH*a};pT(}C0o0!*1DkQqj&w;?F_ooeSeIkfjoo11Bk{D6RaMb zSglqO_fG*F6}NafNWB$H+d97JyL|E9V*tRa$;z>i(ue?HSyq8y7ZL`6BgKkdc6QZB zJsLd6r)r%IydVfPqzD8dJp=?PBF`2NPnpz0Zx}l2>bUWNK%m%gtbhv;1DN(_KwA@C zNBDzJrBVnF-QSVoPLlsdQ1=f5&w~yR=lJmI>S_Q7(%6NjQ_=<L`PV-uzXTWnF<--4 TLZ-uP00000NkvXXu0mjf69%q; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_n.png b/Templates/BaseGame/game/tools/forestEditor/images/erase-element-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..05fb6690f0c3adf1d24688e67d3484420e44ddcf GIT binary patch literal 619 zcmV-x0+juUP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!4M{{nRCwCFRWVD$Kp4$w)e0iGmV%*M zhk~x1+PQ-j#NA(@zre*GAmZqtlZ(2#D@70&wIs!*v=K^6ONr8<Z7zxL<V!iyq$UVG zc--ZhzW2TF`|iSA*Hz{*7L+-Sxd|nLHZz$_Y@%E)S9Tm{N7J+edB0w-PYabwrQd>O zv)RdBtyW`x(tJK&ux)z_*c3Aa#Iu5W?}i1!f|?=7Q2_&lhafBqQmhp*%g}XQl!6f) zzzuX1Hj2gKDTt2*xqt}HTT6JrtIJe(5xoW2k(&V`We^|9q@E++o{rbofK_tj#_BqV zoCZWch4Fa&L5?PAi!1~;DumQ1LZi`0CvrQs5C~LXdhUUzfV6$K+ii`=r$qj21|l+n z^Ios_Vw&bhI-P#;3vteGyi7}uqTdWi+bc*ityZf#91b^9sniLG*L`lZe!qX=#TMph zrck9)F*}{kF_s(2WYS=a?fXT#-R|CCF!+|-*s)Wms_G?NJsO5_jVtC3<X^x$1Psfv zo*RwEEp?ef0$e^jk%WW@rnhFZd4+kgTCMI$r(U%2irAM##=t13KA}P+q9Wo0iQFsp zBK?m^g%=`2a^%KwR0g(*EJ1LkVsnB+VwUu*#1T4!n<o9fL?T=gvLuIIm(BcC&$_)6 zR0l@6=YNp}4e$1z)!trRcv#E+`wJ@_J|1?`<BlHz1^`%e#sim!B^3Yw002ovPDHLk FV1m`~6>9(h literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_d.png b/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..79745e5bdfa39d4744476b68aec9eeaa0aa52b70 GIT binary patch literal 908 zcmV;719SX|P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#E=fc|RCwCFmdj65K@`Tn`<Q!y+LDSz zLIW0xF`_jX7Z`~PjEO<r5aYrJe}F4PLc+!^d);6(F-8_{Yg}L=L=qPeqHau7d=Q(U zJd{!pQfPbc9p}s~SP3s%%V{P<CuM%~oyW{kdV71D07S(Ip$MiZ3N%fFuIn(E5n~2x z*9ll^8liBw0e-(9s;Z(`Ha8a!&CVsF1eL&}kqEp#pFB~q%(5&D4h)2(EK*o(AQ|n? z7=wh8ON$L$HN!NaVwa5xak~yxgjfGN^v#EUJbHF(>(~|o_%uC=sl;a_7H9B1{srSd z#=tWiSDL$E+cq4|p^>t6W%$p~@!3}xnRtTtKS$wM08eq)X)&iAaEpsIj!45<tfW%% zVgO}pA<+J@I~bXH3MZq0+mNa_fvxN;)@prWB$ty=#K$ZVnM&Yww&R$-n;w!t+L#nN zQk6UNoUzYSgG|PP_$Gj=t2STg^RIWo9l*9+m5>VE14y^p1a%Pb)<TiBuSvIVbgk|P z8c%*hJQWu(v7Mdc&K!lvu2t@>m+Dki*-+(<q4UWp36t&=aSWF=q1tTt1G8N>`<NHO zsz!QRWY%h$nt#bS4u^qKoTxgFfD%SUc?b`NZ?36ajcen97FP+WRMo<iR+k?{=czt; zmjhT@NZ{pIAC^~E)?x{YW!Zi-AG_qfpCX=GHl}$)J*FSg{@X~Umrxa~!`tcMYz*B& zz-#Av;PJTcF+JLvt`&A8-GAJLa7_fKo3G;GV7G9zfk1f;_OJ>B{fBV;@CErhiX%N; zhGEFR^M(>ISXqbGmJV5e9j!fb-L`C@JSAsxsUm?oOv9A(Rvn1C9dLeW25o0<NT6j~ z?mtn{tJM4G@_nS6r&Np*vqY$5(!F_K_#Y=;qx1Sb3Cvg-sYn+fDg{!)be7Y6soDD6 zK27w^YKiRRkv3EY>X8<16b}YOCnq`#Z7o^yHI+`jnO(lvEnwVD5?xyZ7Thb*>9;(U z@Py{*ZW$Dy?yo&Xw#5?j3E3WQWR&_w)~$uw)Z`>hF`?9GU--2c9U6KOV$2X-ILj8c iQj8WUCPw>j0R{m3-A2$_!J7C00000<MNUMnLSTXy!<mr) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_h.png b/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5351983f6fe50ae17235ddf6c1e207142a3731f0 GIT binary patch literal 1082 zcmV-A1jYM_P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#+(|@1RCwCFmfwpTRTRhX+<P-K-7)Db zNt?7<*$2^OS&DSAwzN_fkqQ=8Uv%lSPyRPP`l<+3gtkzm;8Lt_MeS}|s!~_c>bgmj zZhkb|WPaRxJm=2rub5;l-SomCFqt`@Ip6bZtfj@p5@T#0&1a)O4YUiAfW>0bFBFR- zC)O$zzf!5pOAXo|&*vGac;ryxtd>`O3BZ_Aior9`R-+LQp}@3Oj9Gm2P|%Ec60~Mo zA)f2eLp3nsNlj3URPO=v#;t|PKmNM+$_M8@x;z++P-J7I>WKkA-@iFgU0W%wcOLC; zw(6xuvp%7sn9WR`{vr%R%g87NSj^h-9Pvz~3eEZ-pnI!J6U!@iUaz-mlR8e)q9SEX z$7o8k>6cIby1u?HEX!i9<0vU5Yys0qNoN(ujBXlszPo$rY`O9G2|y4T0ST-OT$_A( zZub7ewYp87ve|580@|ixQnaR!VAn{0`R9j80@AC}L&j7BgY=io&3M&rrz0HKQ3(S7 z-VP-rs0L*!YFM8xuiiduiXer6slqL=F_d@A&DAubFpuaC)7r-`@l2#KKEwxvy4EiH zK@|A?WHqLe+>+(I!UJ>zm?;cXoBZtrrjaU5kzz3nn$VAH{#I0&H#@f+=)j3o>xhXY zjZ|>a%KOh(NmR?CriUHUZXDtzg)}6m^gjpQE#zMKJ`;N@xv_%}mwsRPaJ#e$rOl5Y zx=Yf6Nu}uw=#3OM{gDj#Mk;NcUMoCzxctV{dmrl-tF)U<_qXzoXQC)_wq(`J7Ebl0 ziDNg(FBd{{ZUMbOuP@>dk3&y2_L|Mdy4S8>Jkt%k&Vl0LdgD=b8aAs|v5asY%uK)a zr7hh6+YY4Q2<5u3zq9~1$KarI<(p6E3i(5S&K#Y+eSPub`>0WOpY2tIlWBRbzw%;f z`X{=NKp81%7%0;j1AHdKf#ik$4fFNFXLGsic;&TM-&~5Kn4g$>=PSA%tC)8lcVryL z$l_q3IgYJd$BhNbOKF86n<iat|DE<%uYEq(4LbhYv*#~|LCB*x;xx6eGEI>Plu-y9 zO#p$U%%BNP66+z`!_4H~QMGaE&D!MbsdqmK!jK0+D5&>r+twyBOF||iHy=u_StdBO z%koIJOuFNGPwBvs8~d{dTOAy0)OsWz$Nr<%5(w!NLNfSvIq4XLzBxU0{M@(L8k{Ol zBo%2g1@N?A4+v#_#?{6l$tYixx1=KXVADgo+6FlT)9ffIxAq2@P9*y7ZM55M-}Ah_ zH*qSEAuaGE2ba4U=TyBH8uj`-+B5sY|5tzk0BH>_;m37=2mk;807*qoM6N<$f{oM+ A4gdfE literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_n.png b/Templates/BaseGame/game/tools/forestEditor/images/forest-editor-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3c305350f5769174b3361a501a96f3669a968cc9 GIT binary patch literal 677 zcmV;W0$TlvP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!M@d9MRCwC#mO*P1K@`V#HnDn;N+tH{ zDt>_CC&<N%mo6d*f}j;o9;$+P6YnBY#FGc9RkT<e1ugaBK?EsYREQ#IE6oQmrY*Ma zyf^-*dE1PVY<4$QL45FdJF}Dh?f<<uvr)zv?@ycW{RR6E{r8~7PB-=idon-l=<$(| znTGG(8b9IAfBd|V4%*F5pFLM5q9LvIZmnqu*JaY$-Z~^1RB(oy)Jogg==lp}?CVix z4gv9)&AJ=Io*fgjH#sprgQZ&q7Qrbu6u_$pXY64DD4f<oOcF5$`~}u*7vQ&cDpNNS zfZ@+TQ5FNKIR|?W?@LN;mhQDGH-Jr-w6eIiO3ga~Q4+M6LS=vuv|z2YqKR5#+*3+b z1=JCkR%u`r*@rjX%p$(DAOp2=J&CC{b@xv77o}b<rA?rzqny_pjip!h`j-!-QfZE) zuC|^w`pUmAKjCglQ$W4fsMX%u;^G0n;O`jRHFzn~QGdKeY+9kXy0qN5;i#mZ(tGX7 z<@&<>r;7mh4-D*_2kgj3N(nVJTwPgNKK$(IY(rqW-QLWk^x$xK6XE&e%7ckxCr?d7 z-B+l8i@92%P@Jnw-ky@B7W5symQ+t`s?$FQd-e+O&CH{RWAcIp=ViA^jJwqWSLGDq zTxYKU0{};4oeN+|qn2wDnC_;sTY5v~`}<|h76EDp8pXZlbgSv<(*_%RQkkyF;jN@r z<Un#;VogP1pVK6{U|GMX>}-La)MbT6X=}AvP_x;<#G3w2`ys#ptsSA8=Eo5W00000 LNkvXXu0mjfz#%`v literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-brush_d.png b/Templates/BaseGame/game/tools/forestEditor/images/new-brush_d.png new file mode 100644 index 0000000000000000000000000000000000000000..df1e11c353d2dcb98bde115b0b833fb1a0869b58 GIT binary patch literal 694 zcmV;n0!jUeP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!SV=@dRCwB?Q_pJ?K@@(IHZ)0vE=06= zS}mxxiiy@AV38Fnno>-?i5Hu{z~ZGhvwQGRL=ZgbL1^(1JtPK-CHSK&A{Bbb5(pZV z+ETTRp?1@x$+pSP`ex{^EwOs=!P~bpyziUu&3i*E%Yy&0X7&jNgZ7%Sk#sr@FFwek zRR@vQA#9l-gu0c;wgj8)PCT4=`AHsN!Yqnz;wFW2+bJNd1XR#a$H-V1Pyx^yK;J2a z*!#VMEfS>`yc%0q9Rg;Pb8`B|Kr(PU76v!$0AJHiSS?rJdw$-S&b)oRTr3<Ym5K+e z)r#@#fxedzVr(e^<px;X<?GVtzI_qmQ}<wgVFt{a=`m~cT78}3KZSPxzL*VW=HfC4 zVN&moKDl}S{l~2SXnX{|F3te0Q*;YVbZTNm_X~S+HhAKBROAZA)u^zx0HU{JcWz?p zp~w<BfP*O~vg7ftlb)2?GlZ9W6kWGVm_(0cKEJuLS}FeqnAAC$DQR*0ptEED@Xw`L z-XYc<*9JkdUsAN`Y^t3lV!c{9zoylyrtT;}vf!aJP_5`rdqWo$l@lOXW^sAtN34`L zB86N@YxcTEfq-D&fydnn;ohrY7-bGd*BS;X^E$hTVb~+MBczF>u><_%wpPC}G<XXo zm$4&td$`ms1g9^6h#`S~`S6hw0jxdA<o6uwgYM3=ZA*GqSSc1HemVsm$47AniM0_4 zBwS1HQXt!;@nn?E#&xd`l9-a<lWD?JBvJY=DGepeZU%1{R}?zWlgS?+!{44WDv<y6 coPP;002b6%?BE_RlmGw#07*qoM6N<$g7MHi4gdfE literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-brush_h.png b/Templates/BaseGame/game/tools/forestEditor/images/new-brush_h.png new file mode 100644 index 0000000000000000000000000000000000000000..57391fe5fa20ce0f2ff2b84db6f93479ab1526a6 GIT binary patch literal 693 zcmV;m0!safP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!S4l)cRCwB?Q_pJ?K@@(ICNxQfE=06= ziWbyb#i;cMSR_J4Q;MlK@nZ8ASiSUSb`Ktk2!ba)2rV9>hr~d!1b;LlQlW<|fuK>T zEmhkz)NYz>vTd@nz8NxWTSO5beEWvC^S$}zy*DHf2tdOu1j%GF@Zy8)x9Z@hbqHG~ zNTDtz0>3fKvcS19a3>y4y!<5hV;n4sFfmBsEd0R;DzLT&heyMJ3V_xCdQU1u#Crys zgOpnEYIHp<6cF~An3Xd(`jdg%u`sw`8+e<x!>V3^?}a&ID*N{Fa;dnlTrTaeRx8G{ z2ik5zh_UGkD3=#vhqqIk{q{wQPu_#M`Drj~rrWI1tMzq?YYJ_?JuyKR=D+}xdUxc> z&HL{^=Cns+!|-)s8fcv&EHDw&M33-EyYhlK{ygeu-Z_?zN^A4r_q6ZKk54}IvyGg< z$&@D&{#fS;cPiaIh{ZjO(2#$fU10(d$$oxwd9|YdBAC=ZnJH;@d7-0y@6eB>8Qvk$ z9S0)W7gW@#T&j(2#QUmxVNI=7P0dz-$&MU24b_Tfw>NY_;hk_GS!RpnmBm=OU_^@f zvg-6WMu31MYT$OYK)B}$7>3Tt2vyX=9;EE++yc5Gx8#bDCX&Gp@RM6xe8%9wEtITd zN9tm@(wh>TIuER(3B;v?hmHsE?nx%U`)Dt8b)0Ek(sI&DsTky^Q_!&eiam*q1VMDc zcPWs?k9ac5X5*U23rXyBf={LiPcezocTrKdzJ-~M<c4uYp>upQfqykrApdQT{TBYm behM%Ea==|nh-Uol00000NkvXXu0mjf8i7No literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-brush_n.png b/Templates/BaseGame/game/tools/forestEditor/images/new-brush_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d51f5d0e91d96fea8a41afb1f3fd5d33e3a9de GIT binary patch literal 636 zcmV-?0)zdDP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!9!W$&RCwB)Q#*(nQ51bMsEk--3X7nP z!B5gitgSYMbrNhukj2VMOc2?2Qz?j|ohij;Q|)diSY+`7b~}}zqAV7&_<=!D$M59c z$2>8zNgxOA-1j@@-Fx31E)t0l4JsH{C=^JwTD7UFYC9YbAHj>sWHL-%K&4V4y3>VF zD0Ew^)jm-*Rb}>p`~^DgW{Og&M9G0b;3$wvr5@P6)9JiG-|cpfYaAYGG#ZaU_5_1L zqSxz*(P$)!qL9gCqI5c)AG6u)?Q*$%x7lozTrT&<=kqD&DR}T>Hk;+qXe8F_bpoF3 z_j>{Aa0wpAxPQaQ3_L6lIFyS<qu&REfm|+^$#^`*HJSqRXrd^#88IG@+h`3EiG;XX zt!yTfsoiWgZ70Q}<WsZT?E;+t0(<XaE(8B7rtlLi-0SrMm^U88a8HRiHy8}WYPDWX zr&B5pps&M&-KW!Osnu$|)^e2=i-ok^Ze=Eb1c$>xppeJtve_(s2_DGTIGE4pH$V)- z4U$f$D{>qsGBV!o^?J#Ay=DT^X&+Fu+wEs?SArmfd7gh{X+Uihzu!+FB4M}NvG5;$ z6R=n;SkYUs&k(8yi1$c#yWPa)a=n6A5uq0@G>!}+-_9dIf#PsDl+a*&7mLNPH)8#M zzlh>m=yynlW|GeZo`86L*5)*#OeTYZ+@Jmhd<6pJrx*5$`Fvg?{{W{I<-hSufB^vV W`*C+7D3XW(0000<MNUMnLSTZu+#m}8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-element_d.png b/Templates/BaseGame/game/tools/forestEditor/images/new-element_d.png new file mode 100644 index 0000000000000000000000000000000000000000..bac080398c2f256d09a111e4828ee111418e3d1f GIT binary patch literal 437 zcmV;m0ZRUfP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzS4l)cRCwB?Q$0?@Fc|%eGBKcVgKz?x z8}tH5?G4(oj7Uu7M}h-Hm8vo}oS<T6paTn2+#4{l5;h=?&9mL4R(?<^mcD#W?DxL+ zJv&S(MgJL|euQCIylXb5CrOfQPtY_NW38aGm;#m2KnhAdr!rOMhGG0jfy}6jIF{ek z(V@P`S2?AV5gmH&X19gQXKWM{0nk+v3t|&e=bV<UEu2hwk0dQzP#IY{UzG&fuyxN2 zm3`=#%XKB`g53Ozf*uxqx1u4cWGLi%^IjDVa8OpU6?shozD>_5Z6>f4QV_*3FNBG_ z=0i^R#r`G*OL%bf!D;pRW{zv%N(aF!<j1WS>HWp@c8!>L2NXH27%)`zXBSp}jbL;` z0aAn*?gzLu0)`6Hf^O*n%Hr9012)H2{7k2}tb?>b^zW{$!`lqkgSnRd7qB%A3UomJ f(=NaBj{pMzh^gxj2u;MT00000NkvXXu0mjfa<I9r literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-element_h.png b/Templates/BaseGame/game/tools/forestEditor/images/new-element_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ddc03f09580e5d8f774d8d93cfed6572e7e1f5 GIT binary patch literal 440 zcmV;p0Z0CcP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzT1iAfRCwB)Q#($>Kop!st|*YXfjI%@ z2Dt!Ia)Zz%lt@(JBj5l|q$rdzCy1yiNI^pt=LS@?m=3VL%dG7<YbWNDCym}}*Yn=I z*|l(87kMZ`=*4U{!(YQ%w<(0EC7k$hRD`yS5%xQ(Z{?j$6<8?gJ;0F>8pOW*f~%oE zVXFk;po`spCsQD*Bb-0+!x$b34JCOHFCleKFs&Woa2PZpF)>L6qGCNS3TVUfnFTxB z)G?NMCux$*{DXof4{ft!TBwq#Fjx?_Og70*(Ix8`tSI2#@D$<t0$GQUg?yTKKof%% z9}?V|oT`E;J=l8>F#mW>ud8iJXMq>W_iG>0+jBbGB{8se;2EQ&WU6Y7&X)2^1-jR8 zm^|WW+u@^$X=t2|phvo=GJn)7lhdnHO5ylM=pSN2v~Dk!4*wRKyYOJFW&ej<szUjX i*dR;=SJ*JV1sDLga_<UtJP5D=0000<MNUMnLSTZ(`MVte literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-element_n.png b/Templates/BaseGame/game/tools/forestEditor/images/new-element_n.png new file mode 100644 index 0000000000000000000000000000000000000000..61f42fb9c6e25e6ebcef002f034e0d746d2d7772 GIT binary patch literal 412 zcmV;N0b~A&P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzK1oDDRCwB)k}+<BKoCWj$W%1s2Be_Q zrlxTL5@m7&r{Wk<Bzm0Aot+a{xq({>r1b%iaskA4R6zbjjJ4W8lz61Cf!%qte-@l9 z%fu8T<j})zw-bMj)!br|B#S^4MM2;9n{ga<p6A=ubh7C}u#-5B0~D@@VYmk*kx4$b zbXk_S?7*={NYj)G3uui70$EVzXXvyzLKudRC_FM?f&6~v`Ww2ix~@qMU>}=%6M*#} z4v-fe(CIz{GuFR_P!|U#jm^Nat4xsBs;bhn4JKG4AD{==SN2^%?<QkApxN%)wmo-U zN1tnBIryaJi$7A+G&I`_^ya#5VOb~mNhzgg8%QYwqyl>OJWrySE|f-~kv_0~u;EJs zz!K>Q@d+p62m3Yn4)$aluoC|aZlJ+##TCH~Ud5I1CBOi*Wjn^K8fDr50000<MNUMn GLSTZjEU!EO literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_d.png b/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c21bb39698df2cb7d3caa927aac5174925abfe91 GIT binary patch literal 709 zcmV;$0y_PPP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!XGugsRCwB?Q(Z_?VHkeC<EV7vIX0(J zE>B7q+YB}gal>*0FI?C~L#3O3q+fy%vx_duAEK+U9Nw(N=pu;-nf=hD0#`SRi?&K; z1+l>_D66eSk_0=w-vMP?BZ59S=lgr#=Y8JyeLkir3jSwk_|Iy!uIxD)<BL!z^zQ)q z24n26j%{a3WhStLnvBTES*r-GCyQkQBV#yd7RkxNKP9b5bRE-V8n2{KQU6AAH5B!e z!vQ4?8G-_1l^v#P8}w2op1*yF_{VvCNi76~Tx$Bd*MyF!af_|5XWyti+8aTcSP7%t z0JozX^U)a;t|@^|+=R4(7c8?2&7JFrE<!|6@}3PszuuspKZ{Ia@Wx@7YHTPkHQ<>0 z5>9#S`26j?Gm8P`)mLBZlFZe1T$|`!Juj0CDvQXwY3#7Jt7o6SE`WlEH8}u|)~2J- z7L}mqVl$?Kx3Kgxh1-t?QLZz<Wj=)SBkgFa>)^J@yF=fT*#HXU+O2j%kHbuKhsU}= z)lUkP(uw2akT;sK-P8i_c()q;4;>IHE?n7<*_V^tF+jBd(oEX5^N58z^T9h&wZV+V z#KIlE7jhURXTlE#2;EAZh!LQN)|PuuuHydF5!Gff5y$MDpEH=q$AC1=BrxSNLGOXW ze0y6?qe8ctih2&gSAHxlr^pUNe(#T1J^-#R9|Y#45EkuP(Xrb>MJ*K7`{A2ibcHab zHn1xBOdT|~6(mRpon*lC4;5=gxyG;!(GPRH8@!mF$Jdwpv))W5fZZ)jP5TsQV@ClO r6n_I2atf2slYy~c;e3zkw*UhGQIqA&uU~qr00000NkvXXu0mjf342C- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_h.png b/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_h.png new file mode 100644 index 0000000000000000000000000000000000000000..705a7a836911350366290c7cf340b1bb01df7155 GIT binary patch literal 722 zcmV;@0xkWCP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!bV)=(RCwByQ(s6^VHE#;SE>AiyE&&( zF0V=ta|Txo*@oo=A6yxv<me?s`b!aF_7ag!qNiAG_+p9CLlO}(`$LNgT)j*b+A5h9 z#0DEdNo_2WB-rhIH#cUEr~~KTkMo`1@B5weosY?~j1^PJ8D9hf0sN~8xq^(bzk2t% zQptW^>)^ThD<@~GKw3%1>0*fh5))vhzlI4+=B}g^k+vh`j{J0Tx`c|RccRTy=peu$ zB?SqB0z;Jrbv0(_#7ew;{{+!b3;2?Z`PP)6YkW)W2pg)+LjwoLrSZWK$~2WQNG5by z`>_z7LBYBbXf<1qQt(PGA>FXH-VN0R4iKey%I<=$RIkQ+_Bi0Ybp)ZXALSeMINo^$ zXWSN08NpQwC@|DiZxf9*7TlN`%&008^{R@{2S0W;bgQw?Ut>^|-vs$t1lEq0V<;*t z!NBD<OwZlH(vKwWK6Rp8tB1{a7#GL7(b8z=xrzI(ZwWZF2^2{6+bqN$g|XI*k%>M~ z_Y;D^#FHl`A#FBdM_oHcC;Ky}|EU0%uJvH{^<xh7(RBeSChp#KROVoA{3fb48L=3T z-CHwAq#!X9d^Ak#4b;pC5xQwDKX`r}4_}O_Ig9ZqX6L<J!Bj2<4KxNKm~xe9JE1Vo zf%dbg&~BxsUclTNFP4^*ln0l0^m`;10iP}p1lCGN$VKZl*1cA0YJo=GAGzH}TL@BV z!>h)g<fx10{dsX3^Il5e^#_WJG*Yd8JHj95`EBrKx}V)#c4UJ&HbY?P5~lqgrM20f zNAnN<24n&SY0wkCiJ!q-k50w%4|mf(Sn;36alZr@02<Ht5H0TJZ~y=R07*qoM6N<$ Ef|!X%G5`Po literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_n.png b/Templates/BaseGame/game/tools/forestEditor/images/new-mesh_n.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c01754ce16f758244ee86fe02dea34b307194e GIT binary patch literal 633 zcmV-<0*3vGP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!8%ab#RCwBqQ@xL2Q4~K<p7>~7G@^Rw zP`pRyBbkv<=vMT*Q~5L2bh<T(LLt;7%q%S#g;HUCMdBk=qTzK^>^V0~%#Piio1DD! z?!CX?Ip=ro5mKoXoYx6WASa*C!)KRxYcm>+{uAW$`6NLQzGX6*w0MquodsMn@p`>< zQ9}0)zb1`FBcm^$PA7)nB^oCXu1nBrwao2yrxEZKFA^A>PA51V4ltX|FquqXv)O>E zs_hRYh@walUhy&u27?@!%jH0lB#1;JP^;BIr_+JSWP;sp_lqzZ5{wMfb6~_*N-vpA zg2&?lgTVmtcpO@-78D8vz%xiB5(Fk}j}u%a@Et*nqBa_hw@pf$F$Rmp0u)8zdqjX_ zy>}%XkH@=kI1GBd9<tdibh}+R91hTIHo;=CKp+s{S@`{a0;~OgU;R@9$>nmvcDv=Z zm&;{9AyqV9lHG0x6c?)1>Ra@81XN$KScK7NM8NiWn}tFlnZTEB*lac`;pYW+t${ii z3?7k4L8Bf&;aBc-IzUE?vKx=b<i32!05wps6AVE=@b?Sab1W7EtJMnidL4$tA=d=Y zvq-YM^^_TsJR`|HMaC9#QH@3;Kde+LymeUK<IP#1H-Lu^NTMQ1i8LvdO0Zt9DKaVk zG@VY<e;p=LcC}i);H#6+f7tK$@@zJvi0XVk@BMcg5yVql;yMpX`!nPGTYv!oIk@*f T4e+V`00000NkvXXu0mjf6J8R! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_d.png b/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..2d2facc7177f3e0b850b5f819ab8014a0b57ee37 GIT binary patch literal 940 zcmV;d15^BoP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#PDw;TRCwB~l|M{VQ543%w(qqK4~PUI zEnrd;1VbYsAtkr~BNiPXNE}Qck--3qSn42y2^%;<8Lfj09hmB(1EbbS1QMf=4n?rV z6g4gM@AID1zNQL-U~h7pzV^QJJLi1ooU8MAJZ%7$#kQeDORv*Gl4Tf;Mwm>-pvh$J zmk3f@TWho1?Wm}zfKD7L<%&ciw%y&GWdbq8X0r*pspKH5)rx2|YSYsLMN%qAI2^{@ z(>W~pmiU=$QVR9jLn$Esmwvb(xuKLP7`i=F_}ySIKq^IgC<@P_2OH}f=;`Uf*w`4d z*=*r|RaO5V6bJ+u5|70h(B0k5kaRlD0KJ}*c79Mi9>;=b0kK#NilSg-cm!QtUC3lI zNT<@ss#zEWL<8y18-;7i1&71IkqizFGK8W?CX+~K(yX~bk{CqarL#ui^?DJGgfTHb z0iVyukoNX=qy!+9PnRsokd1Nya;DS;gF%QX!eTp}PR!2D!u8OFhK2?t5($omOv<v6 z%UP%Yy^^JWsi^Yua)yxDv@}S4T^&P6d>T7{gPaN?<CG>bMsj+H)Xgu<WAecyEEWqA zB3%R_(Fs7H;~*!32(q%Wf);xV`ka06`~6&D(~qZ7Sy{=VbAJfIa}fE&#y_^|2tb?k za8J1zFfcHH(R-r|(R!p9q6vSHj$`x3r_&=MO}x*&JY)&9w!VhIy8xzyCYm1nqJYqR zl=3AU`0@s8-&V1<zY4`vhMA7plR?zw)PTXSK|EQU5oM%<QeS~*Z(rb2bser+n#5fr zF<^f$imm7-R`=c`fde$vG~-UwU0kbf<%qR8dIUu2`ViW{#YPM6_l~1Vxqy^tzP+D& z*#5qa&yg*>{`d+8S#-Qa4XH!3wHeMUeQ33`p~_sv0NRvZZJ-_jIXXKqY>eY(XD=U2 z93FBLHr8CGe{<=|R~P=&Tm(SxWW@-|<kL&lXwc_DB&4<!a~W=3zmc0)^pfz5Jkr9a zPd1WDQcj8>DxcNTRe%V`G>0TnHNl;o9UI+*nn4m=%|mEwgAr^DIip3_K(TQh0rJk# zrXHlhBALym{!l2iOn19hs&r7zs_aeiyejrsEsCJHe~#&;pxFJt1Q-A)3w`F(j@2Fj O0000<MNUMnLSTXg&aZ<2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_h.png b/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2bda1ebbf458f88ba7055fa9fd5f86ed24ffca GIT binary patch literal 1127 zcmV-t1ep7YP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$2}wjjRCwC7mQ6?-TNKCdd}Jaf(Ik`B zw$PgDYvQK5=v%yKC0WImmM#P_q9EwfwJ3Efet@_Umx73kZWI+OR6**h6x$kT1r4Q% zwblBP=tN_plVqmnOmih^OA9HzaL8mb_x$eppN|Q%zP^5i5aI(flfSbhuqV8tD8BOY za<k25`zw@4Bx2s#+3~Tes+z4<D}m2Hw(ae0Q&(4)cW7wnUwSX=+l&zrG=^dR=;Y($ z<F}WWm+w?nRfW2{yI-MCTnNK!4V1}b3I+uN0n_N{sC##J_gZyzbzop%;JGZz3<7Ym zEURiDgcJ&zo13#wPfvTmvC!1iwAR+vwtaqn&cJsLeeyi7>U25<uGNAfibkV)IJw(s zG}<i|3#qTKUu$k|4kQu@4nQpUV*sKc2v<SUEi5e97Z(>jot>S+?CdP5tE*dsXf(*# z*%=G|7>t7BI2B}5Xbte`bef?34s>{Us2>{}bAb~_Utb@Yn3#}VF4uFf*ZVORi*YCy zLW+utGVn|WKq#434CtSSZ@&d?Y;4%TvAd<EMXaf*A-%o5az{tUB2<_J5FQ{b?xzsA z)*u&Ehxy9<Lb_luXeuo&l@T&HI4I)1o}M1Ly}kWuMMZ^le0+?GFenp31VPXsw6rNG zL#ys*5e*LydtjsE;NXCGJRTB{$4P&Gzuel|`V=Nf3?V2LLg2YH<^{H0M3M)>h<=u% zk&zM4*4CB-Wom3}BvVsUC!3p_`%nOW7*$DtV)-(9y<P!JV<hPFdk6)6%MpZ?3!0yw zcOnFmOkkXlnVA_<TU)!)(9rN6N|#F}lQ^~*aIQcU7zrq&Xx*7eo$B{upT&P{j=fya z4-E^$&$P0#;;O8yB;c6%{eH5#x+?p8zRy%D+>A1zTo?)V$;%HfoS%+Al^>l&?eLp? z(^5J4gLD*xYjSeZEsCNL+zpc6{>Ow9SYoi5$ddorUD0GYx^e0H7hVUA2$o2n8p84L z)yV1pb`{cOF<SR-m({E}FI9z7sg&lAs+0#>T3T|3<B@A6S1c!9_q{izhLQwG%1Mcs z@#s{thEF18p=hw0<2l0XIKpx)5lh6qTbCWLZ<RSiqCq@CNwEr(sU$}u$62R==w$e> z))h($vhc|L;2q`yI-Q4;VQYu6(lHms#l<L>*7dmw$HIWzaR>op@g9yK1jD}kp=vBP zoIk9;|CS~kCooEk9Zt2*&au1-%_f_n`J)hwgjQl+M068G!DO3I$=b!~(HZ`ZC#A0e znv1M+LifNul@<aWsuIjGGb{~+gAnAw&1$v@NiK+fQV>G$^k}$;MoZtYk|g2t63+1_ t`}_MoFmv_+)HZKZZvPef=j5vZ0|4U-B~vU#`f>mO002ovPDHLkV1g6n4`BcR literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_n.png b/Templates/BaseGame/game/tools/forestEditor/images/paint-forest-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ad9242eadd90d9c2443631fae01e8efa2937291c GIT binary patch literal 754 zcmV<O0uB9%P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!lu1NERCwCFmOqFZK@`Skbu}uijbL$B zS{z~#2x1+hwO}1YunYzZ3$eRgKoBgg@`t35fQ^O8r5Dl}E`dNmQk(?T6b8b%f_l2U z`JT+exKX)y$qhW-I?TM^eDBS^RWb~N-Nvx7TN<+YAW71V0tJIX=61V}@K}U>o6Tm= z|BE6(9>TqWsaD8_qC-a{5+M~Quqf;uAg2}qbO{XkhLAXiD6msN&NxKoUWcs?1_3~U zPhzoHd^8%d;c!Th_Z;Bh5RHS3gK9)7yk76yN~IE4Rh1x51;`!lO(mKMlK)ag5h_yo ze4f>6HMQ645#$30IQUWmT#1ltg8(^(dr~f!j{#!2Tuz-#CXWEQz`Tpcp`c6HLQL7U zK>S9j1ZlNe1Y1KcFE8`+`TV;8(e^?1f&ignr>NM8M1oO03WWke9t#kOL$0`Og{&Kb z+3Up4KoGbTr74+AvTC(D!tYPIuD@O^7QgrbC9cAR7`q@_Zyg{hf~3=FR<GCBN-$ob z9@Slte!ss~lF4M0Y&IL++v|jy6vN>#BT%>7W$ku*x?C<lQkUTXCe&j|kW#6XvfJ(V zk)1fN>4Uwg1L#>G5QvV)W9D+Xm@LaoQIx62<N1O>7n-Jh#{A0ZbZYQ^_<NX-W1Rwj z4m-2iY@J4<F%{a+rXoriHb+tDk>BrsiK(ABgcBQ&;DhjY(Ks^>hl3&D`e2wp$GR8r zoo2K7gDbXK-YI<p<n#GD)UyVIL6a|ZasO4=G#3iLkr}?$&OzpF)c#0aC=@a`n61QV zk*^CUG%OHFxLAgz1?juqBXi?QlrGFm!l}hifhGjjt=D}UNar`oOxLzUNCL<ZRP210 ktRTbE-v8M@m)`;m0N9i;7+4c5-v9sr07*qoM6N<$g8!sdeE<Le literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/forestEditor/main.cs b/Templates/BaseGame/game/tools/forestEditor/main.cs new file mode 100644 index 000000000..346365b04 --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/main.cs @@ -0,0 +1,311 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeForestEditor() +{ + echo(" % - Initializing Forest Editor"); + + exec( "./forestEditor.cs" ); + exec( "./forestEditorGui.gui" ); + exec( "./forestEditToolbar.ed.gui" ); + + exec( "./forestEditorGui.cs" ); + exec( "./tools.cs" ); + + ForestEditorGui.setVisible( false ); + ForestEditorPalleteWindow.setVisible( false ); + ForestEditorPropertiesWindow.setVisible( false ); + ForestEditToolbar.setVisible( false ); + + EditorGui.add( ForestEditorGui ); + EditorGui.add( ForestEditorPalleteWindow ); + EditorGui.add( ForestEditorPropertiesWindow ); + EditorGui.add( ForestEditToolbar ); + + new ScriptObject( ForestEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = ForestEditorGui; + }; + + new SimSet(ForestTools) + { + new ForestBrushTool() + { + internalName = "BrushTool"; + toolTip = "Paint Tool"; + buttonImage = "tools/forest/images/brushTool"; + }; + + new ForestSelectionTool() + { + internalName = "SelectionTool"; + toolTip = "Selection Tool"; + buttonImage = "tools/forest/images/selectionTool"; + }; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "1", "ForestEditorSelectModeBtn.performClick();", "" ); // Select + %map.bindCmd( keyboard, "2", "ForestEditorMoveModeBtn.performClick();", "" ); // Move + %map.bindCmd( keyboard, "3", "ForestEditorRotateModeBtn.performClick();", "" ); // Rotate + %map.bindCmd( keyboard, "4", "ForestEditorScaleModeBtn.performClick();", "" ); // Scale + %map.bindCmd( keyboard, "5", "ForestEditorPaintModeBtn.performClick();", "" ); // Paint + %map.bindCmd( keyboard, "6", "ForestEditorEraseModeBtn.performClick();", "" ); // Erase + %map.bindCmd( keyboard, "7", "ForestEditorEraseSelectedModeBtn.performClick();", "" ); // EraseSelected + //%map.bindCmd( keyboard, "backspace", "ForestEditorGui.onDeleteKey();", "" ); + //%map.bindCmd( keyboard, "delete", "ForestEditorGui.onDeleteKey();", "" ); + ForestEditorPlugin.map = %map; +} + +function destroyForestEditor() +{ +} + +// NOTE: debugging helper. +function reinitForest() +{ + exec( "./main.cs" ); + exec( "./forestEditorGui.cs" ); + exec( "./tools.cs" ); +} + +function ForestEditorPlugin::onWorldEditorStartup( %this ) +{ + new PersistenceManager( ForestDataManager ); + + %brushPath = "tools/forestEditor/brushes.cs"; + + if ( !isFile( %brushPath ) ) + %successfulFile = createPath( %brushPath ); + + // This creates the ForestBrushGroup, all brushes, and elements. + exec( %brushpath ); + + if ( !isObject( ForestBrushGroup ) ) + { + new SimGroup( ForestBrushGroup ); + %this.showError = true; + } + + ForestEditBrushTree.open( ForestBrushGroup ); + + if ( !isObject( ForestItemDataSet ) ) + new SimSet( ForestItemDataSet ); + + ForestEditMeshTree.open( ForestItemDataSet ); + + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Forest Editor", "", ForestEditorPlugin ); + + // Add ourselves to the tools menu. + %tooltip = "Forest Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "ForestEditorPlugin", "ForestEditorPalette", expandFilename("tools/forestEditor/images/forest-editor-btn"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( ForestEditorPropertiesWindow, ForestEditorPalleteWindow ); + ForestEditTabBook.selectPage(0); +} + +function ForestEditorPlugin::onWorldEditorShutdown( %this ) +{ + if ( isObject( ForestBrushGroup ) ) + ForestBrushGroup.delete(); + if ( isObject( ForestDataManager ) ) + ForestDataManager.delete(); +} + +function ForestEditorPlugin::onActivated( %this ) +{ + EditorGui.bringToFront( ForestEditorGui ); + ForestEditorGui.setVisible( true ); + ForestEditorPalleteWindow.setVisible( true ); + ForestEditorPropertiesWindow.setVisible( true ); + ForestEditorGui.makeFirstResponder( true ); + //ForestEditToolbar.setVisible( true ); + + //Get our existing forest object in our current mission if we have one + %forestObject = parseMissionGroupForIds("Forest", ""); + if(isObject(%forestObject)) + { + ForestEditorGui.setActiveForest(%forestObject.getName()); + } + + %this.map.push(); + Parent::onActivated(%this); + + ForestEditBrushTree.open( ForestBrushGroup ); + ForestEditMeshTree.open( ForestItemDataSet ); + + // Open the Brush tab. + ForestEditTabBook.selectPage(0); + + // Sync the pallete button state + + // And toolbar. + %tool = ForestEditorGui.getActiveTool(); + if ( isObject( %tool ) ) + %tool.onActivated(); + + if ( !isObject( %tool ) ) + { + ForestEditorPaintModeBtn.performClick(); + + if ( ForestEditBrushTree.getItemCount() > 0 ) + { + ForestEditBrushTree.selectItem( 0, true ); + } + } + else if ( %tool == ForestTools->SelectionTool ) + { + %mode = GlobalGizmoProfile.mode; + switch$ (%mode) + { + case "None": + ForestEditorSelectModeBtn.performClick(); + case "Move": + ForestEditorMoveModeBtn.performClick(); + case "Rotate": + ForestEditorRotateModeBtn.performClick(); + case "Scale": + ForestEditorScaleModeBtn.performClick(); + } + } + else if ( %tool == ForestTools->BrushTool ) + { + %mode = ForestTools->BrushTool.mode; + switch$ (%mode) + { + case "Paint": + ForestEditorPaintModeBtn.performClick(); + case "Erase": + ForestEditorEraseModeBtn.performClick(); + case "EraseSelected": + ForestEditorEraseSelectedModeBtn.performClick(); + } + } + + if ( %this.showError ) + MessageBoxOK( "Error", "Your tools/forestEditor folder does not contain a valid brushes.cs. Brushes you create will not be saved!" ); +} + +function ForestEditorPlugin::onDeactivated( %this ) +{ + ForestEditorGui.setVisible( false ); + ForestEditorPalleteWindow.setVisible( false ); + ForestEditorPropertiesWindow.setVisible( false ); + + %tool = ForestEditorGui.getActiveTool(); + if ( isObject( %tool ) ) + %tool.onDeactivated(); + + // Also take this opportunity to save. + ForestDataManager.saveDirty(); + + %this.map.pop(); + + Parent::onDeactivated(%this); +} + +function ForestEditorPlugin::isDirty( %this ) +{ + %dirty = %this.dirty || ForestEditorGui.isDirty(); + return %dirty; +} + +function ForestEditorPlugin::clearDirty( %this ) +{ + %this.dirty = false; +} + +function ForestEditorPlugin::onSaveMission( %this, %missionFile ) +{ + ForestDataManager.saveDirty(); + + //First, find out if we have an existing forest object + %forestObject = parseMissionGroupForIds("Forest", ""); + + if ( isObject( %forestObject ) ) + { + //We do. Next, see if we have a file already by polling the datafield. + if(%forestObject.dataFile !$= "") + { + //If we do, just save to the provided file. + %forestObject.saveDataFile(%forestObject.dataFile); + } + else + { + //We don't, so we'll save in the same place as the mission file and give it the missionpath\missionName.forest + //naming convention. + %path = filePath(%missionFile); + %missionName = fileBase(%missionFile); + %forestObject.saveDataFile(%path @ "/" @ %missionName @ ".forest"); + } + } + + ForestBrushGroup.save( "tools/forestEditor/brushes.cs" ); +} + +function ForestEditorPlugin::onEditorSleep( %this ) +{ +} + +function ForestEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %hasSelection = false; + + %selTool = ForestTools->SelectionTool; + if ( ForestEditorGui.getActiveTool() == %selTool ) + if ( %selTool.getSelectionCount() > 0 ) + %hasSelection = true; + + %editMenu.enableItem( 3, %hasSelection ); // Cut + %editMenu.enableItem( 4, %hasSelection ); // Copy + %editMenu.enableItem( 5, %hasSelection ); // Paste + %editMenu.enableItem( 6, %hasSelection ); // Delete + %editMenu.enableItem( 8, %hasSelection ); // Deselect +} + +function ForestEditorPlugin::handleDelete( %this ) +{ + ForestTools->SelectionTool.deleteSelection(); +} + +function ForestEditorPlugin::handleDeselect( %this ) +{ + ForestTools->SelectionTool.clearSelection(); +} + +function ForestEditorPlugin::handleCut( %this ) +{ + ForestTools->SelectionTool.cutSelection(); +} + +function ForestEditorPlugin::handleCopy( %this ) +{ + ForestTools->SelectionTool.copySelection(); +} + +function ForestEditorPlugin::handlePaste( %this ) +{ + ForestTools->SelectionTool.pasteSelection(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/forestEditor/tools.cs b/Templates/BaseGame/game/tools/forestEditor/tools.cs new file mode 100644 index 000000000..e6f28e8be --- /dev/null +++ b/Templates/BaseGame/game/tools/forestEditor/tools.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function ForestBrushTool::onActivated( %this ) +{ + ForestEditToolbar.setVisible( true ); + %this.syncBrushToolbar(); +} + +function ForestBrushTool::onDeactivated( %this ) +{ + ForestEditToolbar.setVisible( false ); +} + +function ForestBrushTool::syncBrushToolbar( %this ) +{ + %size = %this.size; + ForestBrushSizeSliderCtrlContainer->slider.setValue( %size ); + ForestBrushSizeTextEditContainer-->textEdit.setValue( %size ); + + %pres = %this.pressure; + ForestBrushPressureSliderCtrlContainer->slider.setValue( %pres ); + ForestBrushPressureTextEditContainer-->textEdit.setValue( mCeil(100 * %pres) @ "%" ); + + %hard = %this.hardness; + ForestBrushHardnessSliderCtrlContainer->slider.setValue( %hard ); + ForestBrushHardnessTextEditContainer-->textEdit.setValue( mCeil(100 * %hard) @ "%"); +} + +function ForestBrushTool::onMouseDown( %this ) +{ + ForestEditTabBook.selectPage( 0 ); +} + +function ForestSelectionTool::onActivated( %this ) +{ +} + +function ForestSelectionTool::onDeactivated( %this ) +{ + %this.clearSelection(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/gui/EditorLoadingGui.gui b/Templates/BaseGame/game/tools/gui/EditorLoadingGui.gui new file mode 100644 index 000000000..7641fd9a0 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/EditorLoadingGui.gui @@ -0,0 +1,72 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(EditorLoadingGui, EditorGuiGroup) { + isContainer = "1"; + Profile = "GuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiControl() { + isContainer = "1"; + Profile = "editorMenu_wBorderProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "277 271"; + Extent = "245 57"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Dialog"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Loading the World Editor..."; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextBoldCenterProfile"; + HorizSizing = "width"; + VertSizing = "center"; + position = "5 19"; + Extent = "236 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function EditorLoadingGui::onWake(%this) +{ + %res = %this.getExtent(); + %resX = getWord(%res, 0); + %resY = getWord(%res, 1); + + %dialog = %this-->Dialog; + %dialogExtent = %dialog.getExtent(); + %dialogWidth = getWord(%dialogExtent, 0); + %dialogHeight = getWord(%dialogExtent, 1); + %dialogPostion = %dialog.getPosition(); + + %posX = (%resX / 2) - (%dialogWidth / 2); + %posY = (%resY / 2) - (%dialogHeight / 2); + %dialog.setPosition(%posX, %posY); +} diff --git a/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.cs b/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.cs new file mode 100644 index 000000000..44345f9f4 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.cs @@ -0,0 +1,183 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------------------------- + +function GetEaseF( %currentEase, %callback, %root ) +{ + GuiEaseEditDlg.init( %currentEase, %callback ); + + if( !isObject( %root ) ) + %root = Canvas; + + %root.pushDialog( GuiEaseEditDlg ); +} + +//============================================================================================= +// GuiEaseEditDlg +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::init( %this, %ease, %callback ) +{ + // Initialize direction popup. + + %directionList = %this-->directionList; + if( !%directionList.size() ) + { + %directionList.add( "InOut", $Ease::InOut ); + %directionList.add( "In", $Ease::In ); + %directionList.add( "Out", $Ease::Out ); + } + + // Initialize type popup. + + %typeList = %this-->typeList; + if( !%typeList.size() ) + { + %typeList.add( "Linear", $Ease::Linear ); + %typeList.add( "Quadratic", $Ease::Quadratic ); + %typeList.add( "Cubic", $Ease::Cubic ); + %typeList.add( "Quartic", $Ease::Quartic ); + %typeList.add( "Quintic", $Ease::Quintic ); + %typeList.add( "Sinusoidal", $Ease::Sinusoidal ); + %typeList.add( "Exponential", $Ease::Exponential ); + %typeList.add( "Circular", $Ease::Circular ); + %typeList.add( "Elastic", $Ease::Elastic ); + %typeList.add( "Back", $Ease::Back ); + %typeList.add( "Bounce", $Ease::Bounce ); + } + + // Set the initial easing curve. + + %this.oldEase = %ease; + %this.setEase( %ease ); + + // Remember callback. + + %this.callback = %callback; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::setEase( %this, %ease ) +{ + %this-->easeView.ease = %ease; + %this-->directionList.setSelected( getWord( %ease, 0 ), false ); + %this-->typeList.setSelected( getWord( %ease, 1 ), false ); + %this-->param1Value.setValue( getWord( %ease, 2 ) ); + %this-->param2Value.setValue( getWord( %ease, 3 ) ); + + %this.onEaseTypeSet(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::onEaseTypeSet( %this ) +{ + switch( %this-->typeList.getSelected() ) + { + case $Ease::Elastic: + %this-->param1Value.setActive( true ); + %this-->param2Value.setActive( true ); + + case $Ease::Back: + %this-->param1Value.setActive( true ); + %this-->param2Value.setActive( false ); + + default: + %this-->param1Value.setActive( false ); + %this-->param2Value.setActive( false ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::onOK( %this ) +{ + eval( %this.callback @ "( \"" @ %this-->easeView.ease @ "\" );" ); + %this.getRoot().popDialog( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::onCancel( %this ) +{ + %this.getRoot().popDialog( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::onSetParam1( %this, %value ) +{ + %easeView = %this-->easeView; + + %ease = %easeView.ease; + %ease = setWord( %ease, 2, %value ); + %easeView.ease = %ease; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDlg::onSetParam2( %this, %value ) +{ + %easeView = %this-->easeView; + + %ease = %easeView.ease; + %ease = setWord( %ease, 3, %value ); + %easeView.ease = %ease; +} + +//============================================================================================= +// GuiEaseEditDirectionList +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditDirectionList::onSelect( %this, %id, %text ) +{ + %easeView = GuiEaseEditDlg-->easeView; + + %ease = %easeView.ease; + %ease = setWord( %ease, 0, %id ); + %easeview.ease = %ease; +} + +//============================================================================================= +// GuiEaseEditTypeList +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEaseEditTypeList::onSelect( %this, %id, %text ) +{ + %easeView = GuiEaseEditDlg-->easeView; + + %ease = %easeView.ease; + %ease = setWord( %ease, 1, %id ); + %easeview.ease = %ease; + + GuiEaseEditDlg.onEaseTypeSet(); +} diff --git a/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.gui b/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.gui new file mode 100644 index 000000000..4c76d8388 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/GuiEaseEditDlg.ed.gui @@ -0,0 +1,332 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(GuiEaseEditDlg,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "GuiEaseEditDlg.onCancel();"; + EdgeSnap = "1"; + text = "Edit Easing Curve"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "334 145"; + Extent = "269 214"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "window"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 27"; + Extent = "95 151"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Direction"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 3"; + Extent = "43 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 20"; + Extent = "83 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "directionList"; + canSaveDynamicFields = "0"; + class = "GuiEaseEditDirectionList"; + className = "GuiEaseEditDirectionList"; + }; + new GuiTextCtrl() { + text = "Type"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 40"; + Extent = "25 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 57"; + Extent = "83 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "typeList"; + canSaveDynamicFields = "0"; + class = "GuiEaseEditTypeList"; + className = "GuiEaseEditTypeList"; + }; + new GuiTextCtrl() { + text = "Param1"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 76"; + Extent = "38 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Param2"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 111"; + Extent = "38 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditSliderCtrl() { + format = "%3.2f"; + range = "-1e+03 1e+03"; + increment = "0.1"; + focusOnMouseWheel = "0"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + text = "-1.00"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 93"; + Extent = "83 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "GuiEaseEditDlg.onSetParam1( $ThisControl.getValue() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "param1Value"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditSliderCtrl() { + format = "%3.2f"; + range = "-1e+03 1e+03"; + increment = "0.1"; + focusOnMouseWheel = "0"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + text = "-1.00"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "6 128"; + Extent = "83 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "GuiEaseEditDlg.onSetParam2( $ThisControl.getValue() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "param2Value"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "67 184"; + Extent = "115 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GuiEaseEditDlg.onOK();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "184 184"; + Extent = "73 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GuiEaseEditDlg.onCancel();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiEaseViewCtrl() { + wrap = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "107 28"; + Extent = "150 150"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "easeView"; + canSaveDynamicFields = "0"; + ease = "1 2 -1 -1"; + easeColor = "0.537255 0.537255 0.537255 1"; + easeWidth = "4"; + axisColor = "0.509804 0.509804 0.509804 1"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/colladaImport.ed.gui b/Templates/BaseGame/game/tools/gui/colladaImport.ed.gui new file mode 100644 index 000000000..30838c76d --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/colladaImport.ed.gui @@ -0,0 +1,1698 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ColladaImportDlg,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "Canvas.popDialog(ColladaImportDlg);"; + EdgeSnap = "1"; + text = ""; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "254 136"; + Extent = "516 447"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Accelerator = "escape"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "window"; + canSaveDynamicFields = "0"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 24"; + Extent = "238 417"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 3"; + Extent = "238 366"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTreeViewCtrl(ColladaImportTreeView) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "0"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "0"; + DragToItemAllowed = "0"; + ClearAllOnSingleSelection = "1"; + showRoot = "1"; + internalNamesOnly = "0"; + objectNamesOnly = "0"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "1 1"; + Extent = "74 63"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "254 24"; + Extent = "254 417"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 3"; + Extent = "254 60"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Nodes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "42 2"; + Extent = "32 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "90 2"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "nodes"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Meshes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "36 22"; + Extent = "38 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "90 22"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "meshes"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Polygons"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "132 22"; + Extent = "47 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "193 22"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "polygons"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Materials"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "135 2"; + Extent = "44 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "193 2"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Materials"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Lights"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "23 41"; + Extent = "52 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "91 41"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "lights"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Animations"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "127 41"; + Extent = "52 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "191 41"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "animations"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 68"; + Extent = "254 153"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "LOD"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "59 6"; + Extent = "22 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "DetectDTS"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 6"; + Extent = "92 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Method used to determine LOD for meshes in the model"; + hovertime = "1000"; + internalName = "lodType"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "196 6"; + Extent = "49 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Detail size for all meshes in this model (when LOD type is SingleSize)"; + hovertime = "1000"; + internalName = "singleDetailSize"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Materials Prefix"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 32"; + Extent = "73 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 32"; + Extent = "148 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ColladaImportTreeView.refresh(\"materials\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "materialPrefix"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Import Nodes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "13 58"; + Extent = "72 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 58"; + Extent = "148 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ColladaImportTreeView.refresh(\"nodes\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "alwaysImport"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Ignore Nodes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 82"; + Extent = "65 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 82"; + Extent = "148 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ColladaImportTreeView.refresh(\"nodes\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "neverImport"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Import Meshes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "13 106"; + Extent = "72 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 106"; + Extent = "148 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ColladaImportTreeView.refresh(\"nodes\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "alwaysImportMesh"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Ignore Meshes"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "13 130"; + Extent = "72 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "97 130"; + Extent = "148 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ColladaImportTreeView.refresh(\"nodes\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "neverImportMesh"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 226"; + Extent = "254 105"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Override up_axis"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 7"; + Extent = "102 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.updateOverrideUpAxis($ThisControl.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Overrides the <up_axis> specified in the DAE file"; + hovertime = "1000"; + internalName = "overrideUpAxis"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "151 6"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "upAxis"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Override scale"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 27"; + Extent = "92 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.updateOverrideScale($ThisControl.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Overrides the <unit> scale specified in the DAE file"; + hovertime = "1000"; + internalName = "overrideScale"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "151 27"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "scale"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Ignore bone scaling"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 48"; + Extent = "114 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Ignores <scale> elements within <node>s to fix issues with some models"; + hovertime = "1000"; + internalName = "ignoreNodeScale"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Center model"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 67"; + Extent = "82 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Translates model so the origin is at the center"; + hovertime = "1000"; + internalName = "adjustCenter"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Floor model"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "151 67"; + Extent = "72 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Translates model so the origin is at the bottom"; + hovertime = "1000"; + internalName = "adjustFloor"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Force update materials.cs"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 86"; + Extent = "148 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Forces update of materials.cs (even if Materials already exist)"; + hovertime = "1000"; + internalName = "forceUpdateMaterials"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 338"; + Extent = "254 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Add lights to scene"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 5"; + Extent = "148 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Loads the lights from the DAE file and adds them to the current scene."; + hovertime = "1000"; + internalName = "loadLights"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "Load from .cfg"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 368"; + Extent = "86 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.readDtsConfig();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Save to .cfg"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 395"; + Extent = "86 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.writeDtsConfig();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "159 368"; + Extent = "86 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.onOK();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Load the COLLADA model"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "159 395"; + Extent = "86 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ColladaImportDlg.onCancel();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Exit without loading the COLLADA model"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +new GuiControl(ColladaImportProgress,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiWindowCtrl() { + internalName = "window"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Importing cowboy.dae"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "362 338"; + Extent = "300 92"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiProgressBitmapCtrl() { + internalName = "progressBar"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiRLProgressBitmapProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "10 34"; + Extent = "280 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + internalName = "progressText"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "10 62"; + Extent = "280 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; + +function ColladaImportTreeView::onDefineIcons(%this) +{ + // Set the tree view icon indices and texture paths + %this._imageNone = 0; + %this._imageNode = 1; + %this._imageMesh = 2; + %this._imageMaterial = 3; + %this._imageLight = 4; + %this._imageAnimation = 5; + %this._imageExNode = 6; + %this._imageExMaterial = 7; + + %icons = ":" @ // no icon + "tools/gui/images/ColladaImport/iconNode:" @ // normal node + "tools/gui/images/ColladaImport/iconMesh:" @ // mesh + "tools/gui/images/ColladaImport/iconMaterial:" @ // new material + "tools/gui/images/ColladaImport/iconLight:" @ // light + "tools/gui/images/ColladaImport/iconAnimation:" @ // sequence + "tools/gui/images/ColladaImport/iconIgnoreNode:" @ // ignored node + "tools/gui/images/ColladaImport/iconExistingMaterial"; // existing material + + %this.buildIconTable( %icons ); +} + +function ColladaImportDlg::showDialog(%this, %shapePath, %cmd) +{ + %this.path = %shapePath; + %this.cmd = %cmd; + + // Only allow loading lights if creating a new scene object + %canLoadLights = (strstr(%this.cmd, "EWCreatorWindow.create") != -1); + + // Check for an existing TSShapeConstructor object. Need to exec the script + // manually as the DAE resource may not have been loaded yet + %csPath = filePath(%this.path) @ "/" @ fileBase(%this.path) @ ".cs"; + if (isFile(%csPath)) + exec(%csPath); + + %this.constructor = ShapeEditor.findConstructor(%this.path); + + // Only show the import dialog if required. Note that 'enumColladaScene' will + // fail if the COLLADA file is missing, or a cached.dts is available. + $collada::forceLoadDAE = EditorSettings.value("forceLoadDAE"); + if ( (fileExt(%shapePath) $= ".dts") || + !enumColladaForImport(%shapePath, ColladaImportTreeView) ) + { + eval(%cmd); + $collada::forceLoadDAE = false; + + // Load lights from the DAE if possible + if (%canLoadLights && (%this.constructor > 0) && (%this.constructor.loadLights == 1)) + %this.loadLights(); + + return; + } + $collada::forceLoadDAE = false; + + // Initialise GUI + ColladaImportTreeView.onDefineIcons(); + + %this-->window.text = "COLLADA Import:" SPC %this.path; + + %this-->upAxis.clear(); + %this-->upAxis.add("X_AXIS", 1); + %this-->upAxis.add("Y_AXIS", 2); + %this-->upAxis.add("Z_AXIS", 3); + + %this-->lodType.clear(); + %this-->lodType.add("DetectDTS", 1); + %this-->lodType.add("SingleSize", 2); + %this-->lodType.add("TrailingNumber", 3); + + %this-->loadLights.setActive(%canLoadLights); + + // Set model details + %this-->nodes.setText(ColladaImportTreeView._nodeCount); + %this-->meshes.setText(ColladaImportTreeView._meshCount); + %this-->polygons.setText(ColladaImportTreeView._polygonCount); + %this-->materials.setText(ColladaImportTreeView._materialCount); + %this-->lights.setText(ColladaImportTreeView._lightCount); + %this-->animations.setText(ColladaImportTreeView._animCount); + + %this.updateOverrideUpAxis(false); + %this.updateOverrideScale(false); + + if (%this.constructor > 0) + { + if (%this.constructor.upAxis !$= "DEFAULT") + { + %this-->upAxis.setText(%this.constructor.upAxis); + %this.updateOverrideUpAxis(true); + } + if (%this.constructor.unit > 0) + { + %this-->scale.setText(%this.constructor.unit); + %this.updateOverrideScale(true); + } + + %this-->lodType.setText(%this.constructor.lodType); + %this-->singleDetailSize.setText(%this.constructor.singleDetailSize); + %this-->materialPrefix.setText(%this.constructor.matNamePrefix); + %this-->alwaysImport.setText(strreplace(%this.constructor.alwaysImport, "\t", ";")); + %this-->neverImport.setText(strreplace(%this.constructor.neverImport, "\t", ";")); + %this-->alwaysImportMesh.setText(strreplace(%this.constructor.alwaysImportMesh, "\t", ";")); + %this-->neverImportMesh.setText(strreplace(%this.constructor.neverImportMesh, "\t", ";")); + %this-->ignoreNodeScale.setStateOn(%this.constructor.ignoreNodeScale); + %this-->adjustCenter.setStateOn(%this.constructor.adjustCenter); + %this-->adjustFloor.setStateOn(%this.constructor.adjustFloor); + %this-->forceUpdateMaterials.setStateOn(%this.constructor.forceUpdateMaterials); + %this-->loadLights.setStateOn(%this.constructor.loadLights); + } + else + { + // Default settings + %this-->lodType.setText("DetectDTS"); + %this-->singleDetailSize.setText("2"); + %this-->materialPrefix.setText(""); + %this-->alwaysImport.setText(""); + %this-->neverImport.setText(""); + %this-->alwaysImportMesh.setText(""); + %this-->neverImportMesh.setText(""); + %this-->ignoreNodeScale.setStateOn(0); + %this-->adjustCenter.setStateOn(0); + %this-->adjustFloor.setStateOn(0); + %this-->forceUpdateMaterials.setStateOn(0); + %this-->loadLights.setStateOn(0); + } + + Canvas.pushDialog(%this); + + ColladaImportTreeView.refresh("all"); +} + +function ColladaImportDlg::readDtsConfig(%this) +{ + %filename = filePath( %this.path ) @ "/" @ fileBase( %this.path ) @ ".cfg"; + %filename2 = filePath( %this.path ) @ "/" @ "dtsScene.cfg"; + + %fo = new FileObject(); + if ( %fo.openForRead( %filename ) || %fo.openForRead( %filename2 ) ) + { + %alwaysImport = ""; + %neverImport = ""; + + %mode = "none"; + while ( !%fo.isEOF() ) + { + %line = trim( %fo.readLine() ); + + if ( %line $= "AlwaysExport:" ) // Start of the AlwaysExport list + %mode = "always"; + else if ( %line $= "NeverExport:" ) // Start of the NeverExport list + %mode = "never"; + else if ( startswith( %line, "+" ) || startswith( %line, "-" ) ) // Boolean parameters (not supported) + %mode = "none"; + else if ( startswith( %line, "=" ) ) // Float and integer parameters (not supported) + %mode = "none"; + else if ( !startswith( %line, "//" ) ) // Non-commented lines + { + switch$ (%mode) + { + case "always": + %alwaysImport = %alwaysImport TAB %line; + case "never": + %neverImport = %neverImport TAB %line; + } + } + } + %fo.close(); + + %alwaysImport = strreplace( trim( %alwaysImport ), "\t", ";" ); + %neverImport = strreplace( trim( %neverImport ), "\t", ";" ); + + %this-->alwaysImport.setText( %alwaysImport ); + %this-->neverImport.setText( %neverImport ); + } + else + { + error( "Failed to open " @ %filename @ " or " @ %filename2 @ " for reading" ); + } + + %fo.delete(); +} + +function ColladaImportDlg::writeDtsConfig(%this) +{ + %filename = filePath( %this.path ) @ "/" @ fileBase( %this.path ) @ ".cfg"; + + %fo = new FileObject(); + if ( %fo.openForWrite( %filename ) ) + { + // AlwaysImport + %fo.writeLine("AlwaysExport:"); + %alwaysImport = trim( strreplace( %this-->alwaysImport.getText(), ";", "\t" ) ); + %count = getFieldCount( %alwaysImport ); + for (%i = 0; %i < %count; %i++) + %fo.writeLine( getField( %alwaysImport, %i ) ); + %fo.writeLine(""); + + // NeverImport + %fo.writeLine("NeverExport:"); + %neverImport = trim( strreplace( %this-->neverImport.getText(), ";", "\t" ) ); + %count = getFieldCount( %neverImport ); + for (%i = 0; %i < %count; %i++) + %fo.writeLine( getField( %neverImport, %i ) ); + %fo.writeLine(""); + + %fo.close(); + } + else + { + error( "Failed to open " @ %filename @ " for writing" ); + } + + %fo.delete(); +} + +function ColladaImportDlg::updateOverrideUpAxis(%this, %override) +{ + %this-->overrideUpAxis.setStateOn(%override); + %this-->upAxis.setActive(%override); + if (!%override) + %this-->upAxis.setText(ColladaImportTreeView._upAxis); +} + +function ColladaImportDlg::updateOverrideScale(%this, %override) +{ + %this-->overrideScale.setStateOn(%override); + %this-->scale.setActive(%override); + if (!%override) + %this-->scale.setText(ColladaImportTreeView._unit); +} + +function ColladaImportTreeView::refresh(%this, %what) +{ + %shapeRoot = %this.getFirstRootItem(); + %materialsRoot = %this.getNextSibling(%shapeRoot); + %animRoot = %this.getNextSibling(%materialsRoot); + + // Refresh nodes + if ((%what $= "all") || (%what $= "nodes")) + { + // Indicate whether nodes will be ignored on import + %this._alwaysImport = strreplace(ColladaImportDlg-->alwaysImport.getText(), ";", "\t"); + %this._neverImport = strreplace(ColladaImportDlg-->neverImport.getText(), ";", "\t"); + %this._alwaysImportMesh = strreplace(ColladaImportDlg-->alwaysImportMesh.getText(), ";", "\t"); + %this._neverImportMesh = strreplace(ColladaImportDlg-->neverImportMesh.getText(), ";", "\t"); + %this.refreshNode(%this.getChild(%shapeRoot)); + } + + // Refresh materials + if ((%what $= "all") || (%what $= "materials")) + { + %matPrefix = ColladaImportDlg-->materialPrefix.getText(); + %id = %this.getChild(%materialsRoot); + while (%id > 0) + { + %baseName = %this.getItemValue(%id); + %name = %matPrefix @ %baseName; + + // Indicate whether material name is already mapped + %this.editItem(%id, %name, %baseName); + %mapped = getMaterialMapping(%name); + if (%mapped $= "") + { + %this.setItemTooltip(%id, "A new material will be mapped to this name"); + %this.setItemImages(%id, %this._imageMaterial, %this._imageMaterial); + } + else + { + %this.setItemTooltip(%id, %mapped SPC "is already mapped to this material name"); + %this.setItemImages(%id, %this._imageExMaterial, %this._imageExMaterial); + } + + %id = %this.getNextSibling(%id); + } + } + + // Refresh animations + if ((%what $= "all") || (%what $= "animations")) + { + %id = %this.getChild(%animRoot); + while (%id > 0) + { + %this.setItemImages(%id, %this._imageAnim, %this._imageAnim); + %id = %this.getNextSibling(%id); + } + } +} + +function ColladaImportTreeView::refreshNode(%this, %id) +{ + while (%id > 0) + { + switch$ (%this.getItemValue(%id)) + { + case "mesh": + // Check if this mesh will be ignored on import + if (strIsMatchMultipleExpr(%this._alwaysImportMesh, %this.getItemText(%id)) || + !strIsMatchMultipleExpr(%this._neverImportMesh, %this.getItemText(%id)) ) + { + %this.setItemTooltip(%id, ""); + %this.setItemImages(%id, %this._imageMesh, %this._imageMesh); + } + else + { + %this.setItemTooltip(%id, "This mesh will be ignored on import"); + %this.setItemImages(%id, %this._imageExNode, %this._imageExNode); + } + + case "light": + %this.setItemImages(%id, %this._imageLight, %this._imageLight); + + case "node": + // Check if this node will be ignored on import + if (strIsMatchMultipleExpr(%this._alwaysImport, %this.getItemText(%id)) || + !strIsMatchMultipleExpr(%this._neverImport, %this.getItemText(%id)) ) + { + %this.setItemTooltip(%id, ""); + %this.setItemImages(%id, %this._imageNode, %this._imageNode); + } + else + { + %this.setItemTooltip(%id, "This node will be ignored on import"); + %this.setItemImages(%id, %this._imageExNode, %this._imageExNode); + } + } + + // recurse through children and siblings + %this.refreshNode(%this.getChild(%id)); + %id = %this.getNextSibling(%id); + } +} + +function ColladaImportDlg::onCancel(%this) +{ + Canvas.popDialog(%this); + ColladaImportTreeView.clear(); +} + +function ColladaImportDlg::onOK(%this) +{ + Canvas.popDialog(%this); + ColladaImportTreeView.clear(); + + // Need to create a TSShapeConstructor object if any settings are not + // at the default values + if ((%this-->overrideUpAxis.getValue() != 0) || + (%this-->overrideScale.getValue() != 0) || + (%this-->lodType.getText() !$= "DetectDTS") || + (%this-->singleDetailSize.getText() !$= "2") || + (%this-->materialPrefix.getText() !$= "") || + (%this-->alwaysImport.getText() !$= "") || + (%this-->neverImport.getText() !$= "") || + (%this-->alwaysImportMesh.getText() !$= "") || + (%this-->neverImportMesh.getText() !$= "") || + (%this-->ignoreNodeScale.getValue() != 0) || + (%this-->adjustCenter.getValue() != 0) || + (%this-->adjustFloor.getValue() != 0) || + (%this-->forceUpdateMaterials.getValue() != 0) || + (%this-->loadLights.getValue() != 0)) + { + if (%this.constructor <= 0) + { + // Create a new TSShapeConstructor object + %this.constructor = ShapeEditor.createConstructor(%this.path); + } + } + + if (%this.constructor > 0) + { + // Store values from GUI + if (%this-->overrideUpAxis.getValue()) + %this.constructor.upAxis = %this-->upAxis.getText(); + else + %this.constructor.upAxis = "DEFAULT"; + + if (%this-->overrideScale.getValue()) + %this.constructor.unit = %this-->scale.getText(); + else + %this.constructor.unit = -1; + + %this.constructor.lodType = %this-->lodType.getText(); + %this.constructor.singleDetailSize = %this-->singleDetailSize.getText(); + %this.constructor.matNamePrefix = %this-->materialPrefix.getText(); + %this.constructor.alwaysImport = strreplace(%this-->alwaysImport.getText(), ";", "\t"); + %this.constructor.neverImport = strreplace(%this-->neverImport.getText(), ";", "\t"); + %this.constructor.alwaysImportMesh = strreplace(%this-->alwaysImportMesh.getText(), ";", "\t"); + %this.constructor.neverImportMesh = strreplace(%this-->neverImportMesh.getText(), ";", "\t"); + %this.constructor.ignoreNodeScale = %this-->ignoreNodeScale.getValue(); + %this.constructor.adjustCenter = %this-->adjustCenter.getValue(); + %this.constructor.adjustFloor = %this-->adjustFloor.getValue(); + %this.constructor.forceUpdateMaterials = %this-->forceUpdateMaterials.getValue(); + %this.constructor.loadLights = %this-->loadLights.getValue(); + + // Save new settings to file + ShapeEditor.saveConstructor( %this.constructor ); + } + + // Load the shape (always from the DAE) + $collada::forceLoadDAE = true; + eval(%this.cmd); + $collada::forceLoadDAE = false; + + // Optionally load the lights from the DAE as well (only if adding a new shape + // to the scene) + if (%this-->loadLights.getValue()) + %this.loadLights(); +} + +function ColladaImportDlg::loadLights(%this) +{ + // Get the ID of the last object added + %obj = MissionGroup.getObject(MissionGroup.getCount()-1); + + // Create a new SimGroup to hold the model and lights + %group = new SimGroup(); + loadColladaLights(%this.path, %group, %obj); + + // Delete the SimGroup if no lights were found. Otherwise, add the model to + // the group as well. + if (%group.getCount() > 0) + { + %group.add(%obj); + %group.bringToFront(%obj); + MissionGroup.add(%group); + if (EditorTree.isVisible()) + { + EditorTree.removeItem(EditorTree.findItemByObjectId(%obj)); + EditorTree.buildVisibleTree(true); + } + } + else + { + %group.delete(); + } +} + +function updateTSShapeLoadProgress(%progress, %msg) +{ + // Check if the loading GUI is visible and use that instead of the + // separate import progress GUI if possible + if ( isObject(LoadingGui) && LoadingGui.isAwake() ) + { + // Save/Restore load progress at the start/end of the import process + if ( %progress == 0 ) + { + ColladaImportProgress.savedProgress = LoadingProgress.getValue(); + ColladaImportProgress.savedText = LoadingProgressTxt.getValue(); + + ColladaImportProgress.msgPrefix = "Importing " @ %msg; + %msg = "Reading file into memory..."; + } + else if ( %progress == 1.0 ) + { + LoadingProgress.setValue( ColladaImportProgress.savedProgress ); + LoadingProgressTxt.setValue( ColladaImportProgress.savedText ); + } + + %msg = ColladaImportProgress.msgPrefix @ ": " @ %msg; + + %progressCtrl = LoadingProgress; + %textCtrl = LoadingProgressTxt; + } + else + { + // Show/Hide gui at the start/end of the import process + if ( %progress == 0 ) + { + ColladaImportProgress-->window.text = "Importing" SPC %msg; + %msg = "Reading file into memory..."; + Canvas.pushDialog(ColladaImportProgress); + } + else if ( %progress == 1.0 ) + { + Canvas.popDialog(ColladaImportProgress); + } + + %progressCtrl = ColladaImportProgress-->progressBar; + %textCtrl = ColladaImportProgress-->progressText; + } + + // Update progress indicators + if (%progress == 0) + { + %progressCtrl.setValue(0.001); + %textCtrl.setText(%msg); + } + else if (%progress != 1.0) + { + %progressCtrl.setValue(%progress); + %textCtrl.setText(%msg); + } + + Canvas.repaint(33); +} + + +// Convert all COLLADA models that match the given pattern (defaults to *) to DTS +function convertColladaModels(%pattern) +{ + // Force loading the COLLADA file (to ensure cached DTS is updated) + $collada::forceLoadDAE = true; + + %fullPath = findFirstFile("*.dae"); + while (%fullPath !$= "") + { + // Check if this file is inside the given path + %fullPath = makeRelativePath(%fullPath, getMainDotCSDir()); + if ((%pattern $= "") || strIsMatchMultipleExpr(%pattern, %fullPath)) + { + // Load the model by creating a temporary TSStatic + echo("Converting " @ %fullPath @ " to DTS..."); + %temp = new TSStatic() { + shapeName = %fullPath; + collisionType = "None"; + }; + %temp.delete(); + } + + %fullPath = findNextFile("*.dae"); + } + + $collada::forceLoadDAE = false; +} diff --git a/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui b/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui new file mode 100644 index 000000000..d8f15e76b --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/colorPicker.ed.gui @@ -0,0 +1,1149 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiColorPickerCtrl(ColorPickerDlg,EditorGuiGroup) { + displayMode = "Dropper"; // this makes the background visible + actionOnMove = "1"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiWindowCtrl(GuiPickerDlg) { + text = "Color Picker"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "DoColorPickerCancelCallback(); ColorPickerDlg.getRoot().popDialog(ColorPickerDlg);"; + position = "170 100"; + extent = "439 317"; + minExtent = "8 2"; + horizSizing = "windowRelative"; + vertSizing = "windowRelative"; + profile = "ToolsGuiWindowProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapBorderCtrl(){ // color blend + position = "3 24"; + extent = "255 258"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiColorPickerCtrl(ColorBlendSelect) { + baseColor = "1 0 0 1"; + pickColor = "0 0 0 1"; + selectorGap = "1"; + displayMode = "BlendColor"; + actionOnMove = "1"; + position = "1 0"; + extent = "255 258"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "updateRGBValues(1);"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl(){ // Hue + position = "263 23"; + extent = "25 261"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiColorPickerCtrl(ColorRangeSelect) { + baseColor = "1 0 0 1"; + pickColor = "1 0 0 1"; + selectorGap = "1"; + displayMode = "VertColor"; + actionOnMove = "1"; + position = "1 1"; + extent = "21 257"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "updatePickerBaseColor(1);"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "New"; + position = "306 22"; + extent = "26 14"; + profile = "GuiDefaultProfile"; + }; + new GuiBitmapBorderCtrl(){ // new old color + position = "292 37"; + extent = "52 99"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSwatchButtonCtrl(myColor){ // New Color // + position = "1 1"; + extent = "50 50"; + profile = "GuiDefaultProfile"; + }; + new GuiSwatchButtonCtrl(oldColor){ // Old Color // + position = "1 48"; + extent = "50 50"; + profile = "GuiDefaultProfile"; + }; + }; + new GuiTextCtrl() { + text = "Old"; + position = "310 138"; + extent = "26 14"; + profile = "GuiDefaultProfile"; + }; + new GuiBitmapBorderCtrl(){ // Color Text Fields + position = "291 165"; + extent = "141 118"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { // rgb + position = "4 0"; + extent = "52 75"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "R"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 6"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Red Channel color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_R_Val) { // Red Channal + text = "0"; + maxLength = "4"; + position = "14 6"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerRGBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Red Channel color value."; + }; + new GuiTextCtrl() { + text = "G"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "4 29"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Green Channel color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_G_Val) { // Green Channal + text = "0"; + maxLength = "4"; + position = "14 29"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerRGBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Green Channel color value."; + }; + new GuiTextCtrl() { + text = "B"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 52"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Blue Channel color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_B_Val) { // Blue Channal + text = "0"; + maxLength = "4"; + position = "14 52"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerRGBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Blue Channel color value."; + }; + }; + new GuiControl() { + position = "71 0"; + extent = "61 75"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "H"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 6"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Hue Channel color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_H_Val) { // Hue Channal + text = "0"; + maxLength = "4"; + position = "14 6"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerHSBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Hue Channel color value."; + }; + new GuiTextCtrl() { + text = "o"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "51 2"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "S"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "4 29"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Saturation Channel color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_S_Val) { // Saturation Channal + text = "0"; + maxLength = "4"; + position = "14 29"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerHSBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Saturation Channel color value."; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "51 29"; + extent = "10 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "B"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 52"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Brightness Channel color value. Aka value or lightness."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_Br_Val) { // Brightness Channal + text = "0"; + maxLength = "4"; + position = "14 52"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + class = "ColorPickerHSBClass"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Brightness Channel color value. Aka value or lightness."; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "51 52"; + extent = "10 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + position = "3 87"; + extent = "138 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "#"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "3 5"; + extent = "8 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Hex representation of Red, Green, Blue Color value."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(HexColor_Val) { // Hex Color Field + text = "0"; + maxLength = "6"; + position = "13 5"; + extent = "116 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Hex representation of Red, Green, Blue Color value."; + command = "$thisControl.onKeyDown();"; + }; + }; + }; + new GuiBitmapBorderCtrl(){ // alpha + position = "3 287"; + extent = "429 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "-1 3"; + extent = "428 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "AggregateControl"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiSliderCtrl(ColorAlphaSelect) { + range = "0 1"; + ticks = "0"; + value = "1"; + position = "5 3"; + extent = "341 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiSliderProfile"; + visible = "1"; + active = "1"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); updateColorPickerAlpha( $ThisControl.getValue() );"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "slider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Alpha"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "355 0"; + extent = "28 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl(Channel_A_Val) { // Alpha Channal + text = "0"; + maxLength = "4"; + position = "392 0"; + extent = "34 18"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfileNumbersOnly"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); updateColorPickerAlpha( $ThisControl.getValue() );"; + internalName = "TextEdit"; + }; + }; + }; + new GuiButtonCtrl() { + text = "Select"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "349 37"; + extent = "84 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "DoColorPickerCallback();"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "349 68"; + extent = "84 24"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "DoColorPickerCancelCallback();"; + Clickable = "1"; + AffectChildren = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "use sRGB"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "360 105"; + extent = "66 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$displayAsSRGB"; + command = "useSRGBctrl($displayAsSRGB);"; + }; + }; +}; +//--- OBJECT WRITE END --- + +$ColorPickerCallback = ""; // Control that we need to update +$ColorPickerCancelCallback = ""; +$ColorPickerUpdateCallback = ""; +$ColorCallbackType = 1; // ColorI + +function useSRGBctrl(%colorScale) +{ +ColorPickerDlg.useSRGB = %colorScale; +ColorRangeSelect.useSRGB = %colorScale; +ColorBlendSelect.useSRGB = %colorScale; +myColor.useSRGB = %colorScale; +oldColor.useSRGB = %colorScale; +} + +// This function pushes the color picker dialog and returns to a callback the selected value +function GetColorI( %currentColor, %callback, %root, %updateCallback, %cancelCallback ) +{ + $ColorPickerSignal = 1; + $ColorPickerCallback = %callback; + $ColorPickerCancelCallback = %cancelCallback; + $ColorPickerUpdateCallback = %updateCallback; + $ColorCallbackType = 1; // ColorI + + oldColor.color = ColorIntToFloat( %currentColor ); + myColor.color = ColorIntToFloat( %currentColor ); + + ColorRangeSelect.showReticle = true; + ColorBlendSelect.showReticle = true; + + // Set the range according to int + ColorAlphaSelect.range = "0 255"; + + // Set the RGBA displays accordingly + %red = getWord(%currentColor, 0); + %green = getWord(%currentColor, 1); + %blue = getWord(%currentColor, 2); + %alpha = getWord(%currentColor, 3); + + //Set the red green blue text fields + Channel_R_Val.setValue(%red); + Channel_G_Val.setValue(%green); + Channel_B_Val.setValue(%blue); + + //Have the rgb text fields update the rest + Channel_R_Val.onValidate(); + + if(!isObject(%root)) + %root = Canvas; + + %root.pushDialog(ColorPickerDlg); + + // update the alpha value first + ColorAlphaSelect.setValue( %alpha ); + Channel_A_Val.setText( %alpha ); +} + +function GetColorF( %currentColor, %callback, %root, %updateCallback, %cancelCallback ) +{ + $ColorPickerSignal = 1; + $ColorPickerCallback = %callback; + $ColorPickerCancelCallback = %cancelCallback; + $ColorPickerUpdateCallback = %updateCallback; + $ColorCallbackType = 2; // ColorF + + oldColor.color = %currentColor; + myColor.color = %currentColor; + + ColorRangeSelect.showReticle = true; + ColorBlendSelect.showReticle = true; + + // Set the range according to float + ColorAlphaSelect.range = "0 1"; + + // Set the RGBA displays accordingly + %red = mRoundColour(getWord(%currentColor, 0), 3); + %green = mRoundColour(getWord(%currentColor, 1), 3); + %blue = mRoundColour(getWord(%currentColor, 2), 3); + %alpha = mRoundColour(getWord(%currentColor, 3), 3); + + //Set the red green blue text fields + Channel_R_Val.setValue(%red); + Channel_G_Val.setValue(%green); + Channel_B_Val.setValue(%blue); + + //Have the rgb text fields update the rest + Channel_R_Val.onValidate(); + + if(!isObject(%root)) + %root = Canvas; + %root.pushDialog(ColorPickerDlg); + + // update the alpha value first + ColorAlphaSelect.setValue( %alpha ); + Channel_A_Val.setText( %alpha ); +} + +function ColorPickerRGBClass::onValidate(%this) +{ + %red = Channel_R_Val.getValue(); + %green = Channel_G_Val.getValue(); + %blue = Channel_B_Val.getValue(); + + //Rest of the fields just do everything with ints so convert + if( $ColorCallbackType != 1 ) + { + %rgb = ColorFloatToInt(%red SPC %green SPC %blue SPC "1.0"); + %red = getWord(%rgb, 0); + %green = getWord(%rgb, 1); + %blue = getWord(%rgb, 2); + } + + //Update all the other color fields + %hsb = ColorRGBToHSB(%red SPC %green SPC %blue); + Channel_H_Val.setValue(getWord(%hsb, 0)); + Channel_S_Val.setValue(getWord(%hsb, 1)); + Channel_Br_Val.setValue(getWord(%hsb, 2)); + + %hex = ColorRGBToHEX(%red SPC %green SPC %blue); + HexColor_Val.setValue(%hex); + HexColor_Val.onKeyDown(); + + //Update everything else with our new color + setColorInfo(); +} + +function ColorPickerHSBClass::onValidate(%this) +{ + %hue = Channel_H_Val.getValue(); + %saturation = Channel_S_Val.getValue(); + %brightness = Channel_Br_Val.getValue(); + + //Update all the other color fields + %rgb = ColorHSBToRGB(%hue SPC %saturation SPC %brightness); + %hex = ColorRGBToHEX(%rgb); + HexColor_Val.setValue(%hex); + HexColor_Val.onKeyDown(); + + //convert to float for rgb if we need to + if( $ColorCallbackType != 1 ) + { + %rgb = ColorIntToFloat(%rgb); + } + %red = getWord(%rgb, 0); + %green = getWord(%rgb, 1); + %blue = getWord(%rgb, 2); + Channel_R_Val.setValue(%red); + Channel_G_Val.setValue(%green); + Channel_B_Val.setValue(%blue); + + //Update everything else with our new color + setColorInfo(); +} + +function HexColor_Val::onKeyDown(%this) +{ + //Get the value + %value = %this.getValue(); + + //It's hex so keep it all uppercase + %value = strupr(%value); + %pos = %this.getCursorPos(); + %this.setValue(%value); + %this.setCursorPos(%pos); + + //Verify that it's a hex value + %value = stripChars(%value, "0123456789ABCDEF"); + if(%value $= "") + { + %this.validText(); + } + else + { + %this.invalidText(false); + } +} + +function HexColor_Val::onValidate(%this) +{ + //if the current text is invalid don't do anyting + if(!%this.isValidText()) + { + %this.invalidText(true); + return; + } + + //Get the current value + %hex = %this.getValue(); + + //Make sure we have 6 characters + while(strlen(%hex) < 6) + { + %hex = "0" @ %hex; + } + %hex = strupr(%hex); + + //Update the value in case there were missing characters + %this.setValue(%hex); + + //Update all the other color fields + %rgb = ColorHEXToRGB(%hex); + %hsb = ColorRGBToHSB(%rgb); + + //convert to float for rgb if we need to + if( $ColorCallbackType != 1 ) + { + %rgb = ColorIntToFloat(%rgb); + } + + %red = getWord(%rgb, 0); + %green = getWord(%rgb, 1); + %blue = getWord(%rgb, 2); + Channel_R_Val.setValue(%red); + Channel_G_Val.setValue(%green); + Channel_B_Val.setValue(%blue); + + Channel_H_Val.setValue(getWord(%hsb, 0)); + Channel_S_Val.setValue(getWord(%hsb, 1)); + Channel_Br_Val.setValue(getWord(%hsb, 2)); + + //Update everything else with our new color + setColorInfo(); +} + +// This function is used to update the text controls at the top +function setColorInfo() +{ + %red = Channel_R_Val.getValue(); + %green = Channel_G_Val.getValue(); + %blue = Channel_B_Val.getValue(); + + if( $ColorCallbackType == 1) + %rgb = ColorIntToFloat(%red SPC %green SPC %blue SPC "255"); + else + %rgb = %red SPC %green SPC %blue SPC "1.0"; + + $ColorPickerSignal = 0; + + //Convert color over to hue color + %hsb = ColorRGBToHSB(ColorFloatToInt(%rgb)); + %tempColor = ColorHSBToRGB( getWord(%hsb, 0) SPC 100 SPC 50); + %tempColor = ColorIntToFloat(setWord(%tempColor, 3, 255)); + + //Make sure all the text fields and everything don't update because of the cursors + ColorRangeSelect.update = false; + ColorBlendSelect.update = false; + + //Set values for the hue color picker + ColorRangeSelect.baseColor = %tempColor; + ColorRangeSelect.pickColor = %tempColor; + ColorRangeSelect.updateColor(); + + //Set the cursor for the hue picker + ColorRangeSelect.setSelectorColor(%tempColor); + + //Set the values for the gradient color picker + ColorBlendSelect.baseColor = %tempColor; + ColorBlendSelect.pickColor = %rgb; + ColorBlendSelect.updateColor(); + + //Set the cursor for the gradiant color picker + ColorBlendSelect.setSelectorColor(%rgb); + + //Update our current color + %alpha = getWord(myColor.color, 3); + myColor.color = setWord(%rgb, 3, %alpha); +} + +// return mycolor.color +function DoColorPickerCallback() +{ + eval( $ColorPickerCallback @ "(\"" @ constructNewColor(mycolor.color, $ColorCallbackType) @"\");" ); + ColorPickerDlg.getRoot().popDialog(ColorPickerDlg); +} + +function DoColorPickerCancelCallback() +{ + ColorPickerDlg.getRoot().popDialog( ColorPickerDlg ); + if( $ColorPickerCancelCallback !$= "" ) + eval( $ColorPickerCancelCallback @ "(\"" @ constructNewColor( oldColor.color, $ColorCallbackType ) @ "\");" ); +} + +function DoColorPickerUpdateCallback() +{ + if( $ColorPickerUpdateCallback !$= "" ) + eval( $ColorPickerUpdateCallback @ "(\"" @ constructNewColor( myColor.color, $ColorCallbackType ) @ "\");" ); +} + +// this is called from ColorRangeSelect.updateColor +function updatePickerBaseColor( %location ) +{ + if(!ColorRangeSelect.update) + { + ColorRangeSelect.update = true; + return; + } + + if( $ColorPickerSignal && %location ) + %pickColor = ColorRangeSelect.baseColor; + else + %pickColor = ColorRangeSelect.pickColor; + $ColorPickerSignal = 0; + + %red = getWord(%pickColor, 0); + %green = getWord(%pickColor, 1); + %blue = getWord(%pickColor, 2); + %alpha = getWord(%pickColor, 3); + + ColorBlendSelect.baseColor = %red SPC %green SPC %blue SPC "1.0"; + ColorBlendSelect.updateColor(); +} + +// this is called from ColorBlendSelect.updateColor +function updateRGBValues( %location ) +{ + if(!ColorBlendSelect.update) + { + ColorBlendSelect.update = true; + return; + } + + //update the color based on where it came from + if( $ColorPickerSignal && %location ) + %pickColor = ColorBlendSelect.baseColor; + else + %pickColor = ColorBlendSelect.pickColor; + + //lets prepare the color + %red = getWord(%pickColor, 0); + %green = getWord(%pickColor, 1); + %blue = getWord(%pickColor, 2); + //the alpha should be grabbed from mycolor + %alpha = getWord(myColor.color, 3); + + // set the color! + myColor.color = %red SPC %green SPC %blue SPC %alpha; + + DoColorPickerUpdateCallback(); + + //update differently depending on type + if( $ColorCallbackType == 1 ) + { + %red = mCeil(%red * 255); + %blue = mCeil(%blue * 255); + %green = mCeil(%green * 255); + } + else + { + %red = mFloatLength(%red, 3); + %blue = mFloatLength(%blue, 3); + %green = mFloatLength(%green, 3); + } + + // changes current color values + Channel_R_Val.setValue(%red); + Channel_G_Val.setValue(%green); + Channel_B_Val.setValue(%blue); + + //Rest of the fields just do everything with ints so convert + if( $ColorCallbackType != 1 ) + { + %rgb = ColorFloatToInt(%red SPC %green SPC %blue SPC "1.0"); + %red = getWord(%rgb, 0); + %green = getWord(%rgb, 1); + %blue = getWord(%rgb, 2); + } + + //Update all the other color fields + %hsb = ColorRGBToHSB(%red SPC %green SPC %blue); + Channel_H_Val.setValue(getWord(%hsb, 0)); + Channel_S_Val.setValue(getWord(%hsb, 1)); + Channel_Br_Val.setValue(getWord(%hsb, 2)); + + %hex = ColorRGBToHEX(%red SPC %green SPC %blue); + HexColor_Val.setValue(%hex); + HexColor_Val.onKeyDown(); + + $ColorPickerSignal = 0; +} + +function updateColorPickerAlpha( %alphaVal ) +{ + //lets prepare the color + %red = getWord(myColor.color, 0); + %green = getWord(myColor.color, 1); + %blue = getWord(myColor.color, 2); + %alpha = %alphaVal; + + if( $ColorCallbackType == 1 ) + %alpha = (%alpha / 255); + + myColor.color = %red SPC %green SPC %blue SPC %alpha ; + + DoColorPickerUpdateCallback(); +} + +function constructNewColor(%pickColor, %colorType ) +{ + %red = getWord(%pickColor, 0); + %green = getWord(%pickColor, 1); + %blue = getWord(%pickColor, 2); + %alpha = ColorAlphaSelect.getValue(); + + // Update the text controls to reflect new color + //setColorInfo(%red, %green, %blue, %alpha); + if ( %colorType == 1 ) // ColorI + return mCeil( %red * 255 ) SPC mCeil( %green * 255 ) SPC mCeil( %blue * 255 ) SPC %alpha; + else // ColorF + return %red SPC %green SPC %blue SPC %alpha; +} diff --git a/Templates/BaseGame/game/tools/gui/cursors.ed.cs b/Templates/BaseGame/game/tools/gui/cursors.ed.cs new file mode 100644 index 000000000..7e1ffbf7c --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/cursors.ed.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +new GuiCursor(LeftRightCursor) +{ + hotSpot = "0.5 0"; + renderOffset = "0.5 0"; + bitmapName = "./Images/leftRight"; +}; + +new GuiCursor(UpDownCursor) +{ + hotSpot = "1 1"; + renderOffset = "0 1"; + bitmapName = "./Images/upDown"; +}; + +new GuiCursor(NWSECursor) +{ + hotSpot = "1 1"; + renderOffset = "0.5 0.5"; + bitmapName = "./Images/NWSE"; +}; + +new GuiCursor(NESWCursor) +{ + hotSpot = "1 1"; + renderOffset = "0.5 0.5"; + bitmapName = "./Images/NESW"; +}; + +new GuiCursor(MoveCursor) +{ + hotSpot = "1 1"; + renderOffset = "0.5 0.5"; + bitmapName = "./Images/move"; +}; + +new GuiCursor(TextEditCursor) +{ + hotSpot = "1 1"; + renderOffset = "0.5 0.5"; + bitmapName = "./Images/textEdit"; +}; diff --git a/Templates/BaseGame/game/tools/gui/fileDialogBase.ed.cs b/Templates/BaseGame/game/tools/gui/fileDialogBase.ed.cs new file mode 100644 index 000000000..9d40e0434 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/fileDialogBase.ed.cs @@ -0,0 +1,331 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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 Dialog Base - Add to Sim Callback +// Purpose : Intitialize Variables and Setup State. +//----------------------------------------------------------------------------- +function FileDialogBase::onAdd( %this ) +{ + // Callback function Succeed + %this.SuccessCallback = 0; + // Callback function Cancel + %this.CancelCallback = 0; + + // Multiple Select Flag + %this.MultipleSelect = false; + + // File Extensions Group + %this.FileExtensions = new SimGroup(); + + %this.AddFilter("*.*","All Files"); +} + +//----------------------------------------------------------------------------- +// File Dialog Base - Remove from Sim Callback +// Purpose : Destroy Resources. +//----------------------------------------------------------------------------- +function FileDialogBase::onRemove( %this ) +{ + + // Remove FileExtensions Group + if ( isObject( %this.FileExtensions ) ) + %this.FileExtensions.delete(); + + // Remove Returned Files Group + if( isObject( %this.ReturnFiles ) ) + %this.ReturnFiles.delete(); +} + +//----------------------------------------------------------------------------- +// File Dialog Base - Show on Screen Callback +// Purpose : Destroy Resources. +//----------------------------------------------------------------------------- +function FileDialogBase::onWake( %this ) +{ + // Necessary + %dirTree = %this.findObjectByInternalName("DirectoryTree", true); + %fileList = %this.findObjectByInternalName("FileList", true); + %filterList = %this.findObjectByInternalName("FilterList", true); + %cancelButton = %this.findObjectByInternalName("CancelButton", true); + %okButton = %this.findObjectByInternalName("OkButton", true); + + // Optional + %fileName = %this.findObjectByInternalName("FileName", true); + + // Check for functionality Components. + if( !isObject( %dirTree ) || !isObject( %fileList ) || !isObject( %filterList ) ) + { + error("FileDialogBase::onWake - Unable to find NECESSARY child controls."); + return false; + } + + // Check for button components. + if( !isObject( %cancelButton ) || !isObject( %okButton ) ) + { + error("FileDialogBase::onWake - Unable to find accept and cancel buttons!"); + return false; + } + + // Tag controls so that they can navigate our dialog. + %dirTree.parent = %this; + %fileList.parent = %this; + %filterList.parent = %this; + %okButton.parent = %this; + %cancelButton.parent = %this; + + // Tag optionals + if( isObject( %fileName ) ) + %fileName.parent = %this; + + // Finally, make sure our ReturnFiles group is empty. + if( isObject( %this.ReturnFiles ) ) + %this.ReturnFiles.delete(); + + %this.ReturnFiles = new SimGroup(); + %this.add( %this.ReturnFiles ); + + // If no filters + if( %this.GetFilterCount() == 0 ) + %this.addfilter("*.*","All Files"); + + %this.PopulateFilters(); + +} + +//----------------------------------------------------------------------------- +// File Dialog Base - Add a file extension filter to the list +//----------------------------------------------------------------------------- +function FileDialogBase::AddFilter( %this, %extension, %caption ) +{ + if( !isObject( %this.FileExtensions ) ) + { + error("OpenFileDialog::AddFilter - FileExtensions Group does not exist!"); + return false; + } + + %filter = new ScriptObject() + { + extension = %extension; + caption = %caption; + }; + + // Add to filter list + %this.FileExtensions.add( %filter ); + + return %filter; +} + +//----------------------------------------------------------------------------- +// File Dialog Base - Clear filters by file extension +//----------------------------------------------------------------------------- +function FileDialogBase::ClearFilters( %this ) +{ + if( isObject( %this.FileExtensions ) ) + %this.FileExtensions.delete(); + + %this.FileExtensions = new SimGroup(); +} + + +//----------------------------------------------------------------------------- +// File Dialog Base - Get number of filters +//----------------------------------------------------------------------------- +function FileDialogBase::GetFilterCount( %this ) +{ + if( !isObject( %this.FileExtensions ) ) + return 0; + + // Return Count + return %this.FileExtensions.getCount(); + +} + +//----------------------------------------------------------------------------- +// File Dialog Base - Populate dropdown with filter options +//----------------------------------------------------------------------------- +function FileDialogBase::PopulateFilters( %this ) +{ + %fileExtensions = %this.FileExtensions; + if( !isObject( %fileExtensions ) ) + { + error("OpenFileDialog::PopulateFilters - FileExtensions Group does not exist!"); + return false; + } + + %filterList = %this.findObjectByInternalName("FilterList", true); + if( !isObject( %filterList ) ) + { + error("FileDialogBase::PopulateFilters - Filter List Dropdown not found!"); + return false; + } + + // Clear filter list + %filterList.clear(); + + // Populate List + for( %i = 0; %i < %fileExtensions.getCount(); %i++ ) + { + // Fetch Filter Script Object + %filter = %fileExtensions.getObject( %i ); + + // Add item to list + %filterList.add( %filter.Caption SPC "(" SPC %filter.Extension SPC ")", %filter.getID() ); + } + + // Set First Item to Selected. + %filterList.setFirstSelected(); + + +} + +function FileDialogOkButton::onClick( %this ) +{ + if( !isObject( %this.parent ) ) + { + error("FileDialogBase->FileDialogOkButton::onClick - Unable to find proper parent control! Functionality Compromised!"); + return; + } + + %dirTree = %this.parent.findObjectByInternalName("DirectoryTree", true); + %fileList = %this.parent.findObjectByInternalName("FileList", true); + %filterList = %this.parent.findObjectByInternalName("FilterList", true); + + // Check for functionality Components. + if( !isObject( %dirTree ) || !isObject( %fileList ) || !isObject( %filterList ) ) + { + error("FileDialogOkButton::onClick - Unable to find NECESSARY sibling controls."); + return; + } + + // + // Fetch Path + // + %path = %dirTree.getSelectedPath(); + + // + // Compose File Name + // + %fileNameCtrl = %this.parent.findObjectByInternalName("FileName", true); + + // FileName TextEdit? + if( isObject( %fileNameCtrl ) ) + { + // Get FileName from TextEdit + %fileName = %fileNameCtrl.getText(); + + // Get Filter Object from dropdown list + %filterObj = %filterList.getSelected(); + + // Validate File Extension + if( fileExt( %fileName ) $= "" && isObject( %filterObj ) ) + { + // Append Extension to FileName + %fileName = %fileName @ fileExt( %filterObj.Extension ); + } + } + else + %fileName = %fileList.getSelectedFile(); + + // + // Build Full Path + // + %fullPath = %path @ "/" @ %fileName; + + Canvas.popDialog( %this.parent ); + + // Callback + eval( %this.parent.SuccessCallback @ "(\"" @ %fullPath @"\");" ); + + %parent.SuccessCallback = 0; + + //error("Ok"); + +} + +function FileDialogCancelButton::onClick( %this ) +{ + Canvas.popDialog( %this.parent ); + //error("Cancel"); +} + + +function FileDialogDirectoryTree::onSelectPath( %this, %path ) +{ + %fileList = %this.parent.findObjectByInternalName("FileList", true); + %filterList = %this.parent.findObjectByInternalName("FilterList", true); + + + %filterObj = %filterList.getSelected(); + if( !isObject( %filterObj ) ) + %filter = "*.*"; + else + %filter = %filterObj.Extension; + + %fileList.setPath( %path, %filter ); +} + + +function FileDialogFilterList::onSelect( %this, %id, %text ) +{ + if( !isObject( %id ) ) + { + error("FileDialogFilterList::onSelect - Invalid Filter Object!"); + return; + } + + %fileList = %this.parent.findObjectByInternalName("FileList", true); + + %fileList.setFilter( %id.Extension ); + +} + + +function FileDialogFileList::onDoubleClick( %this ) +{ + //error("DoubleClick"); + %okButton = %this.parent.findObjectByInternalName("OkButton", true); + + if( isObject( %okButton ) ) + %okButton.performClick(); +} + +function FileDialogFileList::onSelect( %this, %listid, %file ) +{ + %fileNameCtrl = %this.parent.findObjectByInternalName("FileName", true); + + // FileName TextEdit? + if( !isObject( %fileNameCtrl ) ) + return; + + // Update our file name to the one selected + %fileNameCtrl.setText( %file ); +} + +function FileDialogFileName::onReturn( %this ) +{ + //error("onReturn"); + %okButton = %this.parent.findObjectByInternalName("OkButton", true); + + if( isObject( %okButton ) ) + %okButton.performClick(); +} diff --git a/Templates/BaseGame/game/tools/gui/guiDialogs.ed.cs b/Templates/BaseGame/game/tools/gui/guiDialogs.ed.cs new file mode 100644 index 000000000..db09e8570 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/guiDialogs.ed.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +exec("./fileDialogBase.ed.cs"); +exec("./openFileDialog.ed.cs"); +exec("./saveFileDialog.ed.cs"); +exec("./saveChangesMBDlg.ed.gui"); +exec("./simViewDlg.ed.gui"); +exec("./colorPicker.ed.gui"); +exec("./materialSelector.ed.gui"); +exec("./scriptEditorDlg.ed.gui"); +exec("./colladaImport.ed.gui"); +exec("./EditorLoadingGui.gui"); +exec("./GuiEaseEditDlg.ed.gui"); +exec("./GuiEaseEditDlg.ed.cs"); +exec("./guiObjectInspector.ed.cs"); +exec("./uvEditor.ed.gui"); +exec("./objectSelection.ed.cs"); +exec("./guiPlatformGenericMenubar.ed.cs"); diff --git a/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.cs b/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.cs new file mode 100644 index 000000000..33b65f6bd --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.cs @@ -0,0 +1,248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// The "Object Inspector" is a useful little window for browsing and editing SimObject +// hierarchies. Be aware that there is no undo in the inspector. + +//--------------------------------------------------------------------------------------------- + +/// Bring up a new inspector window on the given object. +function inspectObject( %object ) +{ + if( !isObject( %object ) ) + { + error( "inspectObject: no object '" @ %object @ "'" ); + return; + } + + // Create a new object inspector window. + exec( "./guiObjectInspector.ed.gui" ); + + if( !isObject( %guiContent) ) + { + error( "InspectObject: failed to create GUI from 'guiObjectInspector.ed.gui'" ); + return; + } + + // Initialize the inspector. + + %guiContent.init( %object ); + + Canvas.getContent().add( %guiContent ); +} + +//============================================================================================= +// GuiObjectInspector +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspector::init( %this, %object ) +{ + if( !%object.isMemberOfClass( "SimSet" ) ) + { + // Complete deletely the splitter and the left-side part of the inspector + // leaving only the field inspector. + + %this.add( %this-->panel2 ); + %this-->splitter.delete(); + %this-->inspector.inspect( %object ); + %this-->methodList.init( %object ); + } + else + { + %treeView = %this-->treeView; + %treeView.inspectorCtrl = %this-->inspector; + %treeView.methodList = %this-->methodList; + + %treeView.open( %object ); + } + + // Set window caption. + + %caption = "Object Inspector - " @ %object.getId() @ " : " @ %object.getClassName(); + + %name = %object.getName(); + if( %name !$= "" ) + %caption = %caption @ " - " @ %name; + + %this.text = %caption; +} + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspector::onClose( %this ) +{ + // Delete us. + %this.schedule( 1, "delete" ); +} + +//============================================================================================= +// GuiObjectInspectorTree +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorTree::onSelect( %this, %object ) +{ + if( isObject( %object ) ) + { + %this.inspectorCtrl.inspect( %object ); + %this.methodList.init( %object ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorTree::onRightMouseUp( %this, %itemId, %mousePos, %object ) +{ + if( !isObject( GuiObjectInspectorTreePopup ) ) + new PopupMenu( GuiObjectInspectorTreePopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenDeclarationInTorsion( %this.object );"; + + object = ""; + }; + + GuiObjectInspectorTreePopup.object = %object; + GuiObjectInspectorTreePopup.showPopup( Canvas ); +} + +//============================================================================================= +// GuiObjectInspectorMethodList +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorMethodList::init( %this, %object ) +{ + %this.clear(); + + %methods = %object.dumpMethods(); + %count = %methods.count(); + %methodsGroup = %this.insertItem( 0, "Methods" ); + %parentScripted = %this.insertItem( %methodsGroup, "Scripted" ); + %parentNative = %this.insertItem( %methodsGroup, "Native" ); + + for( %i = 0; %i < %count; %i ++ ) + { + %name = %methods.getKey( %i ); + %value = %methods.getValue( %i ); + %prototype = getRecord( %value, 2 ); + %fileName = getRecord( %value, 3 ); + %lineNumber = getRecord( %value, 4 ); + %usage = getRecords( %value, 5 ); + + %tooltip = %prototype; + if( isFile( %fileName ) ) + { + %parent = %parentScripted; + %tooltip = %tooltip NL "Declared in: " @ %fileName @ ":" @ %lineNumber; + } + else + %parent = %parentNative; + + %tooltip = %tooltip @ "\n\n" @ %usage; + + %id = %this.insertItem( %parent, %prototype, %fileName NL %lineNumber ); + %this.setItemTooltip( %id, %tooltip ); + } + + %methods.delete(); + + if( %object.isMethod( "getDebugInfo" ) ) + { + %debugInfo = %object.getDebugInfo(); + %count = %debugInfo.count(); + %parent = %this.insertItem( 0, "Debug Info" ); + + for( %i = 0; %i < %count; %i ++ ) + %id = %this.insertItem( %parent, %debugInfo.getKey( %i ) @ ": " @ %debugInfo.getValue( %i ) ); + + %debugInfo.delete(); + } + + %this.sort( 0, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorMethodList::onRightMouseUp( %this, %item, %mousePos ) +{ + %value = %this.getItemValue( %item ); + if( %value $= "" ) + return; + + %fileName = getRecord( %value, 0 ); + %lineNumber = getRecord( %value, 1 ); + + if( isFile( %fileName ) ) + { + if( !isObject( GuiInspectorMethodListPopup ) ) + new PopupMenu( GuiInspectorMethodListPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Jump to Definition in Torsion" TAB "" TAB "EditorOpenFileInTorsion( %this.jumpFileName, %this.jumpLineNumber );"; + + jumpFileName = ""; + jumpLineNumber = ""; + }; + + GuiInspectorMethodListPopup.jumpFileName = %fileName; + GuiInspectorMethodListPopup.jumpLineNumber = %lineNumber; + + GuiInspectorMethodListPopup.showPopup( Canvas ); + } +} + +//============================================================================================= +// GuiObjectInspectorTreeFilter +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorTreeFilter::onWake( %this ) +{ + %treeView = %this.getParent()-->TreeView; + if( isObject( %treeView ) ) + %this.treeView = %treeView; + + Parent::onWake( %this ); +} + +//============================================================================================= +// GuiObjectInspectorTreeFilter +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiObjectInspectorTreeFilterClearButton::onWake( %this ) +{ + %filterText = %this.getParent()-->FilterText; + if( isObject( %filterText ) ) + %this.textCtrl = %filterText; +} diff --git a/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.gui b/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.gui new file mode 100644 index 000000000..596e8e216 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/guiObjectInspector.ed.gui @@ -0,0 +1,401 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCollapseCtrl() { + collapseGroup = "-1"; + collapseGroupNum = "-1"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "1"; + canMaximize = "1"; + minSize = "50 50"; + closeCommand = "$ThisControl.onClose();"; + edgeSnap = "1"; + text = "Object Inspector"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "152 130"; + extent = "658 472"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "GuiObjectInspector"; + className = "GuiObjectInspector"; + + new GuiSplitContainer() { + orientation = "Vertical"; + splitterSize = "2"; + splitPoint = "300 100"; + fixedPanel = "None"; + fixedSize = "100"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "1 21"; + extent = "656 448"; + minExtent = "64 64"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Splitter"; + canSaveDynamicFields = "0"; + + new GuiPanel() { + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "0 0"; + extent = "298 448"; + minExtent = "16 16"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Panel1"; + canSaveDynamicFields = "0"; + + new GuiTextEditCtrl() { + position = "2 3"; + extent = "278 18"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + superClass = "GuiTreeViewFilterText"; + class = "GuiObjectInspectorTreeFilter"; + internalName = "FilterText"; + }; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "281 4"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + superClass = "GuiTreeViewFilterClearButton"; + class = "GuiObjectInspectorTreeFilterClearButton"; + }; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "1 22"; + extent = "297 426"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl() { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "0"; + mouseDragging = "0"; + multipleSelections = "0"; + deleteObjectAllowed = "0"; + dragToItemAllowed = "0"; + clearAllOnSingleSelection = "1"; + showRoot = "1"; + internalNamesOnly = "0"; + objectNamesOnly = "0"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + profile = "ToolsGuiTreeViewProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "1 1"; + extent = "166 21"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "TreeView"; + canSaveDynamicFields = "0"; + class = "GuiObjectInspectorTree"; + }; + }; + }; + new GuiPanel() { + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "302 0"; + extent = "354 448"; + minExtent = "16 16"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "panel2"; + canSaveDynamicFields = "0"; + + new GuiSplitContainer() { + orientation = "Horizontal"; + splitterSize = "2"; + splitPoint = "100 300"; + fixedPanel = "None"; + fixedSize = "100"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 2"; + extent = "354 448"; + minExtent = "64 64"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiPanel() { + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "354 298"; + minExtent = "16 16"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Panel1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiScrollProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "354 298"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiInspector() { + dividerMargin = "5"; + showCustomFields = "1"; + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "1"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + isContainer = "1"; + profile = "GuiInspectorProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "1 1"; + extent = "337 16"; + minExtent = "16 16"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "inspector"; + canSaveDynamicFields = "0"; + class = "GuiObjectInspectorFields"; + className = "GuiObjectInspectorFields"; + superClass = "EditorInspectorBase"; + }; + }; + }; + new GuiPanel() { + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 302"; + extent = "354 146"; + minExtent = "16 50"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "panel2"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiScrollProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "354 146"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl() { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + mouseDragging = "0"; + multipleSelections = "0"; + deleteObjectAllowed = "0"; + dragToItemAllowed = "0"; + clearAllOnSingleSelection = "1"; + showRoot = "1"; + internalNamesOnly = "0"; + objectNamesOnly = "0"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + profile = "ToolsGuiTreeViewProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "1 1"; + extent = "109 42"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "methodList"; + canSaveDynamicFields = "0"; + class = "GuiObjectInspectorMethodList"; + className = "GuiObjectInspectorMethodList"; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.cs b/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.cs new file mode 100644 index 000000000..089b0b8fa --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.cs @@ -0,0 +1,19 @@ +if(isClass(GuiPlatformGenericMenuBar)) +{ + exec("./guiPlatformGenericMenubar.ed.gui"); +} +else +{ + %guiContent = new GuiControl(PlatformGenericMenubar) { + profile = "GuiModelessDialogProfile"; + + new GuiControl() + { + internalName = "menubar"; + extent = "1024 20"; + minExtent = "320 20"; + horizSizing = "width"; + profile = "GuiMenuBarProfile"; + }; + }; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.gui b/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.gui new file mode 100644 index 000000000..8d2cbcd74 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/guiPlatformGenericMenubar.ed.gui @@ -0,0 +1,14 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(PlatformGenericMenubar) { + profile = "GuiModelessDialogProfile"; + + new GuiPlatformGenericMenuBar() + { + internalName = "menubar"; + extent = "1024 20"; + minExtent = "320 20"; + horizSizing = "width"; + profile = "GuiMenuBarProfile"; + }; +}; +//--- OBJECT WRITE END --- \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconAnimation.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconAnimation.png new file mode 100644 index 0000000000000000000000000000000000000000..480dfce703f0aec6aafd8cae5874a43cf4b524ad GIT binary patch literal 480 zcmV<60U!Q}P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzf=NU{RCwB)Q_W7oKoFj8w=Ja_Ihb%V zL7t!_dT*k4^$EZ?&^O=>^bM*{px!ll*F>HGNj#BwAWESvbnA>Qq}^69PBNM7{Cqn* z->eeHF(`@xSvlA{>Y{0*Hc$ts*AFju-E0okkti|%7Q05p=+x`mU{y`<CQ}#=KOpdf zO9VMz34p~7#@Ji4xdTjPOHdIB==a}22(gPGgDe1pyf~&=0SZM}v`{*epW;$JaOpgX zqUfhcmh{HaI)I+<2jKfY=$f{4hO-$=a26Do0m_Eh!8y2FwN2ovip6vI`W(~yPx1X% zYgtG^F3_B1Tsa0-u~0*EqpHl&bsf0IVU8VyVMud%!q^D{jY<L1;zz^KZObyz$1=AG z@VqGmK}Zy~2&9ZgSJSvnbO{kd!bYNWCO^h$Gu^@v5BHC4?Bdnhs21iSzuOy6$hPjE zZ+qDe+MKHp3xQh~{i+nFy!4;s_Q0W=%ND4>d(xpJ1K2c^zW^-q3!6B@ZTpXW3orn+ Ws_E5fA*oIP0000<MNUMnLSTZSfYL$$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconExistingMaterial.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconExistingMaterial.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e4722324ba066a550415999190735f0b8feef8 GIT binary patch literal 918 zcmV;H18Mw;P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#C`m*?RCwBA z{Qv(y10?_;fLPE4f@j`T{{8Py)c-&K;(q@6E%E37KZgGd|6VXK{9E+*&%b4dDy;4z zs|N@mMr0QR%(x!>>(}qeiv0g2&Ey3b_}G}i;vc{NVK{vICBx-sU!OBD{?9$t=)4xD z2Oxmp2KY_C8vN_~?}e_aT--X+ybMp@{$hCX@jJucfBzZyxtJNG_&FGEzxm9te(&9{ z3@i-srzZG;4FL!sumQePE-U^1{pYZ&3b&NH1TVv>`yUwo{Q1qGFE7Zz0F*y>|2+dE z6C;DRBoD*m_n#Ry?7IDo;r~C|Q?tVE0R#{e$P}QTV-)%ROKC{*F&w}Bj^Wq$Ukty0 z{$ubpk!0{Sm16h}H0;;UUkq2Dd|*%z;b%}&;gkCR{dWvVFF*i+4fyr<Z>+h3FvI;9 zUl@M>{tYznFVN|K7}gzs#IP2KfBpW&@axYnhA-d0Gdy_xiNVG|o`Lb#uUL>?fB*u= z0?<ZjL0%Sy+s}bc|NEEW_ut<@7ye_2aZq9ah0yWq&%k^J21bS#Z$B}pNy#w$|NC7U zq!%Cn3FLMH00@Op+kd_WXoCKtZuU5l;D?|h{sqiCoMqhMp-ss~=Tg<WwF1s1Ps>4h zB82DR3Lqv(Wc+>l_UjJ@VQyAnEHQx%0~rE|q5nY5e;ED&z4j026@G4ZhL@i{GcYp# zdkWGC5I_(Ee*IZ|=;{*&c~Nd)dI35W=#)SU1qMwyK?W`kR)*X_eQ?<P0b)f7eul$` z?|=+m4AKe^K#-L9<NK29H$JB;OA1JtsfjWi0UEaI;7x|*`>rwk2ipE0oErZ#7^=%M zJoxa5;ojBT&w##I0@4Z)K=6cQn!F<T|K}eIy@GVOl|*<MZajR=@cP|nhJSzlF$f88 zF{sK412unP*tcf?R|ZDb_*3i4zzGQ;fKZaMY50O*hX21ND{D(i+L$Xd2=lOk!{F7& z9}I_%-DG%l_4ac{MyA|T>&xLu86bcd(Q~6&=nUom|Nlk-Lo$xx-wz2O4Gb_wU}j=i s#K`b_*~#_QD47W$fEX#w&Hw=h0H{BLdxllh{r~^~07*qoM6N<$g3ZFYTmS$7 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconIgnoreNode.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconIgnoreNode.png new file mode 100644 index 0000000000000000000000000000000000000000..744df795af9996f6c1007a592dad168a5e5c5a2d GIT binary patch literal 853 zcmV-b1FHOqP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!=Sf6CRCwBA z{Qv(y10?_;fLPE4p0+l`G5q_R`Ty@feTKh(zyJUHhvEO<znnmPk>Ssu>GBh%Ekafg z5I~H`E&ytdXXWEsAYi1)z{=0V@c;K828O@?7#M+azuvxQczfgw!_QZ5k`!kym=Dtf z5I}GPp0?B{vhZ@v6ExOhVEguzf#KC728MtC7#Ki?{QSwlz%R-0lUIh}^}!PiKVQF1 zR$jDn9!MWR0D%p7+FX~&!UxoB1l0T$sQKAF2B33+*8T<>@Ec;tuU`xd;tC8u1>_iB z9yktiNuuiNO$z`5hzVp0(4U!thMEj)A0IO?Ji5)mASlDYpyLSC_?Lm<JJ5DbD+UH3 zX$FQ{R~Xoz++Yy1Gy;0>S0+d=Kmb7k@y}m9R&H(vhHF5Ze*$gd;Add4@?&7&5@cZb z_8G|bVqjP`1t@=sf#LR526kmVpaH-2L0SO<2yDRrKfk~I|M`=h5oF^JppEAaGBB`k zGBCJ?Lqh7{Mh1q%J0Nn5Obq{i`~n&94I~8+Kwty@f&2?|?)P61O|0yQxMBqw_U9km z0HBLOmV%`K0tn)Q-@myT8Gy0z?Hj}pV|xY$hX4kKU27N^K7U|f2u=Y8EHGA9%wzy5 zV`2p9`NIv;3lKn%F!=rZ^6$6rjoE~O0d?mZ1H-)=3=E4VFfi=f1hfrk)2rveczz6t zP9-gdAFtkkTyPnr6(E41Uidxz)sfS4L{0S>*uQ;eV7LjCyLJ&0jUeysSPwSnkFplS zPbD3OXS??S4fs19q!%E7;0ft&OlS%#FVAc-Gb09$`&WQ20lMP<KX7XN{qqOIKRIQF zU%=3Rxo1Bx4ZKd(Idf$WNFP7|p(N#7!9I!X{JitT%#0b>1o#>L0u#@_e}BON_x;UV zhBx~UGW>l0GD+v`Rd`Yc2p~rE+<4Q=DenKDKfuiNS06}!XFz18OF%qb_w2QW$m#(C fh>_Cl3=m)dj8|3`UT=(b00000NkvXXu0mjfz<zua literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconLight.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconLight.png new file mode 100644 index 0000000000000000000000000000000000000000..bd47c6187339e35249275c5bba78d2950d6ddd2a GIT binary patch literal 503 zcmV<T0SNwyP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUznMp)JRCwB)Q@<|*VHkd{i1-na5;YiX z)=oz0C=wQlQAllu@+X)rWibh>L5e|ZKrI*yotSW)P*f}0YO8%c-`%yjUV8>_^4xph z_dd__-M#moCL*YXrfEi&87we`2)|WI5F6SNc8$7CV=QX0#5)ok5{a#d6e7f)NOVag zI3TLlq_Hp5$rAL5qz^=jmm;NW=5y2Nb(1DuF*r7j2aOTJ+&s`b0<`%6&oD7S^Z`AC z!0Z&PSF4B9z>mNro6$w|v+8B;=5{A@2QV=XTVIT5hcl$Y%MWvbxy)QP*Qlyu6W#d) zI`ms|vqTE0JX7;Q<V?exMD2^X8pIKgQT$505HzT$`!w+hL@yNQ@GpZk9Mj;Dhg6>| zD5E!uPk_4zot&_%0S84lOt!Yo$5&7Wse54mR8bbm$u#!NF-X=hJ<8d%NH#<)i>!~; zhsddE{tC5b#;!)8KLGh6kW8t)WGkYZ0govWc_7E`=f7QPZUz~=QzT#JEWDM>y%wRl t{EZw#M2LV@3bT2u_8zn}_)q)_FaTq_`PI+qmRtY;002ovPDHLkV1i{@+3ElQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMaterial.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMaterial.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c83d94285b577c37382d56d2f01fddba6e5d5f GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^RlY zAw%ze4>_O+XMsm#F#`j)FbFd;%$g$s6l5>)^mS#w$tWT!D>`}6^I)KmWQl7;iF1B# zZfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEyuaw5N+>h{frx!54*qj?muz|Nm2)3*6V{ zE}9;5DaUz<gw^KM^M}LJ%8f(V&2F%Di*h_yYuj}_D%8crqi%ACsKoJ5ZnaO<TxVF< sFOTWi^5eBr)`72+*VQds#d3-9z(*OK8}fbJKx-I0UHx3vIVCg!07x@ZI{*Lx literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMesh.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconMesh.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b7e7f9499968a4f81cebed15caa4f0a52fec3b GIT binary patch literal 767 zcmV<b0s#GqP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!p-DtRRCwBSQ(Z_?VHkeSHrsKVbAO`b zbZ$-?DzXYoNoyDc-c;0;SP0ogP=wV*N#YL#Wm!|YD6h+cLJ$f;2=pq2)*o58LH-x( zXXdc2b7tG={l=M0!|(9$p7Z`Z=i&X{my;w39LFL3sGHy<(?fVb7#9bzf-MfWnLsY~ z5-fyK+5K)Sq74L8tRwT7&(6>Z;WeJ#$E*kSvGD6SR#ze{Wt=ccL_&%{k@ja+>@CR8 z*PUx^VJ?<rs<GFizq1!@&289j&i9b7m2y2B2vWtR1tre=mj-Ys_b41zI}7RfkOAeI zT{z=83*YUx=xpp}$~;tKNLO$<OQUz*Y3YaCRE5&a3fOasnP2-exQv{PJQ$T0WGhWL z-*6FDO96`&r4!WloVp1n*FZ@|r5SG9PUh!7FU>&3si5E#Nhs;5cGa+$YeNr=8Y3lU zKuO80V4tgwxebBE09NB+L=zDRi746Ms4S`YPXPq!4<QOMAwZM_m`z6Jj<G%ry?TNr zaTZ@<^H>mPU@X)n)lnbvR4IZVYe58KOVBE_@GG*6u16irttT{e40L_y$-FgPUAYH4 z%4tmOQD}43h<p=W$vNOpaNZ+_8{Eh1kKya=cl15Hi(n|oBo>twmgCdH$JDh>6Xq4_ zq!kh(=GF;pwH7z)wr^~TT~mc#qt{()x{i}|%~+0vFywoRVc!s5zMn{mW+eV~0{U>v z<LfH67OT9c`?23q54D0vT#94mZwLWl3cVvYF!JmLBVc3Qo$7%gUrSzDwFB*q?Qm+H zkdPoqNjvGEz}3Mk@GtsVem9|y?MAwS*5p+3+fZb+VYjUcRrbAb<Tx=fdJiLSp0n^U x;Q|%TZM95p`F@!_AX|gHjjV8d^EUnyU;w2k?enhRPHX@G002ovPDHLkV1kiDU`_x4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconNode.png b/Templates/BaseGame/game/tools/gui/images/ColladaImport/iconNode.png new file mode 100644 index 0000000000000000000000000000000000000000..a24d6052d0091ca884fba0db05ae610c925e5030 GIT binary patch literal 917 zcmV;G18V$<P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#CrLy>RCwBA z{Qv(y10?_;fLPE4l217*|M~Mf>hIrwaX)|mkofoS4+F!$e=ivRF)aG?_wTZe-q-IU zs|N@mMr0Qxp0Er4`TOT&X$ECUeNkHmZdQJ<_~&0A7*0Ok&2Zz>`R71`a`%QkSPRnw z5I}GP;*Z$`fB*e+p*5d3x4N(a!?W*?8J>N63YL@Nl4g+Llwr8`{1n6Tt4qHEogIHN z=@HlvfB*s;5PR54>F1x{hph#?rBsFW8IHc*&+zBpUm*U+zywkR@+%`VgSD6=!_}v! z7?xaG_6+C}+e_Jx?*Rl5NS@*EpWiXkjLK3Vn?ag?{Q1rB>+f%dKS2EJ&o2gGm@v3X zc{7}PahO3z+Kj<aSV!vTZ-y9<UVs1s8}R$@&sakVJBBCU9sxD|VfYISxPL&J5$FQO zfBzZ$6~Y)CWjq-&bW0eXe7etIuj<V3>nB4jNH0JDferZn^QW``n-Ig(uMdHm|1x+; z`!Hl^6#_LgF$5@vGuTVJfeqb!XC=ea5BC@ZImH<Meg7{F(hCqkEKma&{sF_{KhQ>A zW?lv>2?qu~HhzX$vknF!4iT{CSr>X4Zoj$Az{AP|3=?3$|Axc@KmdVV!0-oX@VD0t z(p<6(Uw(dMn0BF);p^|uV9o#j|6`bSuAAZdtMlN%l;V?Pc>Uoy&~*&pC;|u|NErP7 zzxe3A-3(HkvJ9;M7#LoCea0~HbTh-Pw^teFobP40_WA-)10w?y5X<r@GHkiBivj3} z#UQ-^0R%SS=lB0hPCvWw?DEqS43<(Z3~WG!@4h}~n0}^};o6Jy49x$T7}%JY8SIrj z8BX6j%y8k_&1Ve17?yza0t65|A?Zw#4gUJ`|H5RGAZ{aBYlg=k?*sjH9~>?+f=UcB zeDVw@?;T`VuyxB<CRWDy2YpY#2?-#8P?EAzw`}mgpZ_Nt%c@IyXt@GITLk1(hSy(T zFl@W9i{aAsyU!V!7;_)=KY=G@fB<4d&y5OAvdRp9|3?7>Htzq={}K#;fhm!Z@dZ$7 r5isPJJ(&CiB{Km85F@4886dy_xpiI^gI>TZ00000NkvXXu0mjfoPMTo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_d.png new file mode 100644 index 0000000000000000000000000000000000000000..57abbf499108c6a3d5ae0c04da7604b8ae9fda27 GIT binary patch literal 375 zcmV--0f_#IP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz8A(JzRCwC#mS0W+F%ZVTX>k{{F_Hk! zkaz*l<heY8H!{&DpL`<w2iR@v>{<|(fGq}1w3AFG?PllK`L>fCvex3KP2d+SLL)Rn zBlMp`d&eH$PG>voJN`RlFvyKYrVZvvpF&_yFV9~?yQYX4R&4Z{11Sf?u3h|orCqx+ z^a1<?tLxR`Ew5`7s|xAOkj?{L-vrxIQzgYFjrOcO2>RG-sZC-9q9)>1CL>5JkhHlJ zd(B>)481Sq-dM4eKq)M8=NV5P+m~xCbx~CqjwciheHdtRV#=-dqMbDnSHy>tl6Y5e z#U{OkW*ICF$n7I<1G#V@pI#T8y}j@218q)RxtC8d<8lN+O-K*=7{w9v&q3b>7yyY@ VVikJTW@!Kb002ovPDHLkV1msanLGdh literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f506cda5a02d04b2475b956153372dbd82ffc03d GIT binary patch literal 376 zcmV-;0f+vHP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz8c9S!RCwC#mP>BJFc60Sv1vk!RTT+X zw-OiNOq`1&a3iF4)m2w036I2fC#iX*kHqxF!oM^c*-_^6OgxUsT8qDyz&|KJ12jMb z^m3q`&mN5LA5O*-KBN;Eq(&pr2Fs{RKA^?ZV>7faMa-~bUz;Tmvombk`R`|1SI^KJ z@Bmh~+x1IY6v(zY;=4Zm4s^E*Y(<Tg6#Ed`BlFhKd8eY5ffa~Kk7pSTAhAYN#Zhbp zTXr^dx{~ky?i&fLghlEs;S0d==Gv7y%X9RHBl3pMt~I%0%A?kzlQ9uz#EX-Xcvf)6 zCcc5@GFW_3?xymVkShmj)90c#_u=<#f|e`J-OK05xENkg<J*I6MBz3Vpg*Bk0R{kA Wi((c10I2K$0000<MNUMnLSTY8xTI$Q literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-bottom_n.png new file mode 100644 index 0000000000000000000000000000000000000000..38fcc753dbd320fbfeb780bcbd8e4f8cb845b37b GIT binary patch literal 348 zcmV-i0i*tjP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy{z*hZRCwC#R!a)PFc8gDQ#U<<peVR< zp*L`&$JUMK5EKPhh2lmupT<N|h>5jL6?Kt;$B^lemwD4k3qlC=ZHv$kERY7$KpIH@ zhqPVxU^Ey$tjCk7pp+s)2%-|l!ybHyHoI*#scjg?cBmtfOK|PquhdDzPHNKa^;5<u zQVL{g3LhuM7(12XIBrxqlzmU_o*Ek`+AF(CT=d};n8aCvHtOkYzHm;%Iln{6$taVa zAoJxaq|Rl|Da*iH-t}q;dZb#$Xo0m>G724!40**XxvP4rHp7~z&YVY9>}h|iNQLeX uZ+H(BN_dgxzB5?IAp8vm(ofh;fB^tnwQ0AH&PNIW0000<MNUMnLSTZy%aK<A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_d.png new file mode 100644 index 0000000000000000000000000000000000000000..585cee321c8286b52f299e9d3e722789b4bd78fb GIT binary patch literal 384 zcmV-`0e}99P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwC#mfdQ@Fbsv|pEzq>3T3SC z(Ffim?2QKNRc|*;TH39a<|OuJdQ&@%Qy3GNgjnQ5=SWT%5y8~K;STW+{d-WElJuxG zt)ABGTHfpSZ9M3_s)8ylxScU9F7I4*P~leXujM_}QifbHMjs@-U?KW6acIox<NY{E zBWW(;iopU@e#;K=Hwn7%g7g(jFwSPbbtngA{j}_aTKYCpx6FB(yy&2uU_)dYSJ)mc zG#0_A2UQ%}RZRS+KJz<1S>7f=XUHNr#9-~#IoI1HXl<j4gOk)z>mlCCoa7gYOMk0? zvAXD-UIcncL!5KRW(c$u`!kXh{j;Lb_AB#M{Oo<6Sq38p&-8N8S$m7T0Z3CqaV&Y+ eJ^t{&1sDKgOH3r#M{4E(0000<MNUMnLSTZ1IjSE3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_h.png new file mode 100644 index 0000000000000000000000000000000000000000..88ad2882ac05953dc61bc0d79cf70ba6bd3af016 GIT binary patch literal 382 zcmV-^0fGLBP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAW1|)RCwC#mQ8NLFc5{uKXD31NC>s} z=z)8L-l!5?u`RL!YAK$nli0=T#>9=o0!FeD#iM+h_hy_hB7&)d!xiEVEgCYFu-2MZ zk54R>_x8RS4?3@^phydDTOEtbJ69c4xK;aWc~7;JAy16e1Id>xM4uuKjrrPrd`{9x znv1w%umF|cvbp{yp$jiYU$F$^Z1!7+a!}S!%T}nRZzJ{J8BfcL4$29hiA>`P+oOfX zA{h0cibK1KiT~DT&f_!K+az>`EP_J})^440yiG!D8&w>fq>ftWdMhtYe3iKLw+a}m zi_YnvKrd;CQ_k27g|=cJBT3OeD++DDGT+6|-j|tWFmmt#US^-Qx$*`eO$o&@<z+YX cv;8B$0MW`!BsVE@lmGw#07*qoM6N<$g1BX*ivR!s literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-left_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a86581d315f3a4fe16b819a704f29b386abd377d GIT binary patch literal 368 zcmV-$0gwKPP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz5=lfsRCwC#mQ4$SKoEvk_d_TM0txyH z9g6Dj*TpXV2%|6PDtU--Z8ukUMe4A+xpt@*mH`=foOfnc9nLxES}yp4{!ptzx`gF? z!LL!|=<>qDzCEZ5p>jlk#i%pu_F{}h5M)nA`#nVnt324&4CpH*B#)!<I3D``y^}#A z5-Kas-$TZSX@618o|YA7>dAPs2rXd8T{uw1f4bvZD8fa`WIBr}r65TXK(li>pNKVR zX)k_bK-1o8p|H3d%8D!MXD2o3ZEdKkxFXNAx2SI7b`w+w$+NnrFJjVCrPO3?nnK0U zBo#}Ud}b9YzB1JM-5ab|4rp0!gI*fnwWjteAny{&+O*gCnV;>a00RKdc0kFw22ec! O0000<MNUMnLSTaUpq;V+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_d.png new file mode 100644 index 0000000000000000000000000000000000000000..7ac9b2eb4b0f0b35a135a5c98bf570c7dcba8517 GIT binary patch literal 384 zcmV-`0e}99P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwC#mceSnFbswzIdR&=huynF z3w@ZrWN&+`6}H2=>qf{5h1zP|sI==?I!5Hem>?Jo_J{xfmmOqgNL>UssDJ3+gJ!>7 zIG@hG%&pfiJel0%`P}OHX*lQ=^&&X-Vn`*gX}?0oWS)`$NeyZ?4R7DyI7yI>Rb|~4 z9w0BR1@TiC>I@yWsR7C|&!NxD!Jx0kVvC8vaiz`UiAfE*2*@AA(6p~q({{!qn!6}< z-L(m+K&ybKDDr`^i&EEJf9jlc9PqLSMim*|AQk>%f8xS*bUja(V~=w4I2pkqCh5}{ zn%yU#iS>dc6-C}0Ap-T<&~W~eNVu-1chC#}!TKMWs`1JMkv*tggWvcZ`ee?OPTE^| ei$DCm00RJosz+V1CWbu#0000<MNUMnLSTY<L#<r^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae17f9335c79cb6f4f5c955f441a8e79dd41ad6 GIT binary patch literal 383 zcmV-_0f7FAP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAxT6*RCwC#mceQQF%X7lCcD|Xn?vvR zP{D`sCB5~nTBrx@Rav$|!DPB|M`{&WDLd&w2bMq}`N;qOlPoebq%MMgs5?}(vcD{v zK2Of(R;wkC$4_`X_qKQ+4%(w!1jm*LspK{7SIC&mQxYJlLCv<|ug#j11nE#!)-Cq{ zd1)<(pSn<IXum}bP?mWPeO?X%{V*0=Obm`IZ5~fdYS39g{v?K`eJq-`GhWd=Myczr zO-Kb=1^kI2?-{!&b=~!+&PhiBPxJp!k<krO;ZOD_E?h^~^K?G)C^wIj5gcNYK8>N- z{qmVuFGx~R<m~|>P_GRQ$1jP5>#Fn)cHzHRf3>MYzcN8&4{F!o1)oEo%$dSTdkb&o dXZu@#0RXZ5M_o<`=4$`|002ovPDHLkV1i^|qGkX9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-right_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba926e1954ef09a58898de35cce13dc5cd861aa GIT binary patch literal 363 zcmV-x0hIoUP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz4M{{nRCwC#meC4=FcgM2w`o*Dfdt+a z^akD3yB9^!E7V0s^$b!Msask{5hN7ZSr>I+Yz()L^Z#d?R4GMFhef^+e<*73HjAd? zNoB6*y6P5&v^*DF&ibG=%4Oi}NJvxiva~tS8}rlxpoIn<`Mw%@o-$HM#bPe<q2T<r z6-9>%+s=nwi!oB?r55P*6i_Ifz7V3ZU9F(A`9j4}Orq$Kwul@L`vFKTn}AzEA**u4 zAhklvl(C9+9s3QXCZP_xr`y%CC?M!l$!#O&G%K38*hp~PLV`%rcF!RoZ-Nb#<(C9} zZdAS6F8tkQO`CqJ`<0O(u|&HDKlm)`WabE&)&~8VpY5jr0|5BfItq7`po#zh002ov JPDHLkV1gm{o45b~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_d.png new file mode 100644 index 0000000000000000000000000000000000000000..5380df5c4b274e89b74c63eecf62ca0f28ae9d41 GIT binary patch literal 385 zcmV-{0e=38P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzBS}O-RCwC#mR(N6Fbv1-hP4`-s_m8W zxWjNPF2dVx1rj`QfT=4eAI_4+VPUK25M|S(ShC_aN`Cn}u2r<w!nv)03s{C`==DNx zewuu|TiTDp!iK$Z-*f{68#2%sH);*7RM0BtQXZ{eEbcLVetCQChptM4oDH!X5kn|M zcvAvYx6WNS@7}^sk#oaiKa|arMxORSxK0&6qo`~7xsHmf*}U#p{|$I5)b~l{7f%TH zm0&|TOhxgvjR~_+S?sTr#3w^3o^(ujSc2udC}%F!%B6Y~5)RtA<b(dckd7|M#B4{f ztvJ$yRqvrajdz9RK~o;ndy}l6_L&|4nxs2brWvCCMQNg{O&UwDvZ0jHUpf-zKZgv> f&<yRetpEc6FO6s5#KH`p00000NkvXXu0mjfd$_62 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_h.png new file mode 100644 index 0000000000000000000000000000000000000000..368fcab209d58ddadeef39f8d26c9623fc15ea6f GIT binary patch literal 383 zcmV-_0f7FAP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAxT6*RCwC#m0eE5Fbv1-hP4_@)%MDG z<S-nIi}1#+K!PU@Fm(mx!@*e`7Og`oqI^8Wk`=d6^2^_GUG(0=Z(9X_U<F#Bli1m3 z6StSw{<ZS374P`d>VRNN4m!u9wa{w=y<yjSuztP1!u<L1`RO$DzIG^?P`eQ^hDyaZ zH9&LD&f)6#7Ga9mos6fUVu3UZv>U=ruJ{2}+v>MXQrs+8?UD5Z;23CVS0*gJAlz4h zkEO6s)yEbt&L(B4%~2MQhEh7|oba&x>K9cnIn{<!J&Oqk%dX|1UoWJ=1-X>%1on+Y zcCzX{w7dDPumWfyV}5U1^wU1m6F{?c=gO==%#SF|RP|X?8KrCrrS#p7gykn*paoi> d{|5RlzyQ0`XW%5+zo`HK002ovPDHLkV1gYBu}}a2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/align-top_n.png new file mode 100644 index 0000000000000000000000000000000000000000..be973864767ee8be22cf99c18668d9ed0da9a620 GIT binary patch literal 356 zcmV-q0h|7bP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz21!IgRCwC#mCXvmFbv06XXizRXF*@W zi$0Cwb1I79E9gN*y$iyI9@=cR9VwJ;vQ6j5p@EX6S^4Fkb{!EyIN!F0^8>R`3$4YP z&l2OwR76qaaLygzAU#W-8IxWP0G`CLd<OtB>u+I5%y@q~7DLy|1;I|ZFLtCQM=VE# zz8H$8z*K<;!%<)=-fcI2x7QD};_Kz&=d8~F%Rtq2#noa;X(mZMFFmD|2_qI&p|P&i zb=`E%J0JEPEJ{_RDo~rtaIX46N?Q)Jg&t}<y=hy`X9PiQ1E}|n;bGJ5tp{qf+f)V! z;U7_IsJa`UP$^|~D2?B}k%;|?Td0Lv=-)se0t^6*9B+N5ffxk<0000<MNUMnLSTZ` CIg>~L literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a98c192dd2b35e9578b74091b022976e8e467646 GIT binary patch literal 322 zcmV-I0lof-P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy<Vi$9RCwBA{Qv(y1HAzg!vKI$OGhmo zx|XsKqeys`C()T%u<ZsnkopP4fA2iFPrjuf@HTBbF^&UMAmu>(jUu0t0*uYg9&vJU zG91}|kWocJ5iXAGYpTboxVRXM$qvM<*zBNXLgE2pE+A&6dGI0uc3d7CEgVK@iG-B# z>1QdlXcGoye58Dc6|l0fGB7eSQN^dAvh*#er1|vuGhT%t3z?azW9eT|SqduJp!tsn zDId}%cwyxxv@HDx#D9o1{|{6>#U;{TXbnP4%KrydPqC%{u(_TTz&K(W+W-Lu00Z(h U1@YaO_y7O^07*qoM6N<$f>XwU>Hq)$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c8289f7a02cd67a847945f0c539827c56eb8da87 GIT binary patch literal 323 zcmV-J0lfZ+P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy<w-<ARCwBA{Qv(y1HAzg!vKI$OGhoG z3Sc3mKzNoX(RoU+?FKiH`U%8;?>x9qzNH}WHf=it5sm{>Amu>(jUu0t0*uYg9&vJU zG91}|kWocJ5iXAIYpTYnxVRXM$qvM<xXh<jLgE2pE+A&6dGI0uc6^>1EgVK*iG-B# z>1QdlXcGoye58Dc6|l0fGB7eSQN^dAvh*#er1|vuGhT%t3z?azW9eT|SqduJp!tsn zDId}%cwyxxv@HDx#D9o1`wvt-#U;{TXbnP4s{aR7PqC%{aJiloz&P6Or6m9eFaU)` VH3ff$`<ws(002ovPDHLkV1nnYfdc>l literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/bring-to-front_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0f768fbc6e309b1426e55acc9b0f329489fad785 GIT binary patch literal 325 zcmV-L0lNN)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy=Sf6CRCwBA{Qv(y1HAzg!vKI$OGhoG z3Sc3mpsk~e=sdM!>sD?c^%IEy9y)xGd`m%~s-ld62*-gaVIY1B#NR0LDJeilPwx>2 zCnv+1lP4MNZ0+IV=)R_EoQjHx!I<no%!<o=S|ubNAm##MW|{{t5@5&YsnNn=1eQoh z8J~WZLW?$GP{v2fhgbnC3o8Q?BNJ783MxzAf=ZgtpFiV|E1-o;%+#^;FQ_a96>ZS` z$Agp)X%oD#auZsX{sZDaM4J5vs-EH!=`XYfAtu%TgQ}<4(to&IPYPfhZTHd=00bBS X%D*>O?uk?d00000NkvXXu0mjfQ3ip# literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_d.png new file mode 100644 index 0000000000000000000000000000000000000000..fc95fc3e6660ece54591f701105dbe72e34a449b GIT binary patch literal 621 zcmV-z0+RiSP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!4@pEpRCwCFRy%IOKoA{!y=z>6#0@Nw zDhg7hB%(}{l7=#u$PH5E1}V5gN*W{zP*6%PfGDX*lR!QaMi@-?Eip1CU>k5ovaDUL z_s!?M@fg)=^_+-m^06re(=?43V`LZx2`>6v@QMLI%M^B@a6<Wfo^)NOv}`zhx8H`N z8bB4`S+PjjTuynEmd$1}YBig-3JVdY4G6>ioO4p3NNL)Dz6sMLo&MRl3~q1GoBNmj z*o>?m;`Mw!CvUMJ{Z|&TYa8cKy<Y!gH%pC1V`H~mE*}RJRw|X&H#*&oy-ue?jx+fs zCfgeQ(TOy6#^g*Ksmb})Gl@ZOAG<r()2X|xwdWlV^y%(&1%x-&iJL?`S!7iWqa(+; z-fGo)xK0j=kB};`G)S5tP%u&u>EiNAbvG+xZY5P@dZEK`WIj+Z)j?axdb*NGuItLT zyKzUPWFY)-O?R#RuESMyhKe+NM<i&HPP1}U*A$>-We#f&s8_fnZSEbh4WhMy6c9di zxb&i@NRt|<|IbCLg;@V7gEXiiQW`ADuCXUJC}?kGthG)9S%9TJX@U;o{MNe|i`SIk zeUMbpFjDS$i=CwTGiwIh|Cg%d!1Tluq#@_f0|qU&1P$ph(pgiO{n5urjfY7M>Bh_x zSIe)10bYF>si=H@eb?IUXPa{&r*KY8kwBvqtS_I+j{pMz!yBsfhu|Te00000NkvXX Hu0mjfN0|}% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b6a4015a30f548910264ff2e26d1a129bac671a5 GIT binary patch literal 821 zcmV-51Iqk~P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!*GWV{RCwCFR>5x5KoFf>ukBPWQlLo5 z9f@lw62E}dGnWd9D<9AgXp|o)caCuBg)?8kfeT5lh(kG*NI)tjX`IG8!;Bp##<AT5 zluk6Vb~8KAGxKKFrq}H@0pOUm4!f5t)^j6(jYgyXu+i8#;?KdLJ{Sy+4HDMx9~=PL zXy>4UbNS=4ZioP^wLo`}TVxeH1|>kW07~y3ln?|@6C$7$VBA>Wy!+JN#(t@cBX}m+ zu&mp?br{YRy8y*MW1NkvBX~v*BG<G`zI40oqWxa4dz#;SdU)8m4OH;rNG+?XFl@D2 zf2}`%>#gne`+XQsCJqaU8fQIvN+U%iuu%j`ssAR*uirz@^IW_a?9Y|&t%Js-jfq5Y zq|!D;nw7U7dX?+RWMXEsnVv^;Rp^g#1doS<jTyBG6JEb~vILT+tn(xh=P6r@+r~1H z7&)>CI+xw|AHTM<H7t^RqyW<sR(=lQ)zRb5Iv^yGT9imR^qj%V=Z`x4pSrmCewrms z7APC35Xo`Ondkd<KM3Y4QfYGZid2H9aZ%HvX0{QoMV10(Q@x5LCZ!6)&=I}9o;u=V z=`Vhgj6qELw1B-bw(>QtS}*EIJS?m+1|2Ot6)=_37X&Y^sh~pvm3Z20bq~-2l+%iy z>n$sW7Be=q8iQ3L_1a`ld?lGH_n8a_F{1K-n$2csIt`uCXyoy{HOa1Xa&r@%71>+6 zs9lLqjMGb0+Eb}$rATVz`?R>}Ex4|+e3l29BF(i+ZN*OP%X|u+NgKLS6wuyYZO$WZ z!H5ymYPC6w<GQZp7g!=ln}~gx&*YXtN(oSb$>h+|Z^=b}^z1=r8qRpB6)Tg*wI!q~ zAX_{Jsj{Y}Dc6dU)N(4fI4`ya2yd-vVe|~>`(Bjk&rSO(x!V2(G~3wQoR24xd;pKf z<9ZMTJO41_t1tfG7WSTB4Ts09clL$<M}PqUNxXH|w_yy(00000NkvXXu0mjfu?Kzy literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/centersnap_n.png new file mode 100644 index 0000000000000000000000000000000000000000..189932ef909e5a187e64e55bf4549f4b4fa35313 GIT binary patch literal 443 zcmV;s0Yv_ZP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzT}ebiRCwBA{Qv(y1HAzg!vKI$OKA#N za7m~rDB{$Vnw|51_S7kiq*!$4!F|G(5*bP$i$Tf%A1N9cab+2pPwQG*F%&$1{+vNl zQW8T?Q%yA^)qLvW?1aK!yksec*q*)nKomRF*MIT(p4thC2Z*_Vn3-y+7vw{He5g-Z z2$@TgrLi&5aEm}Rh))i{nUFfWdnqcE(X$kCLL!uUnSiPHFLI(m4pL;Ff)Wxi^-2So zzk&E05dT3=RLGVR3SLI^ID}b*9Mo8%j1`F4pfn@VB@z_;M~_o{d?@$_^)1Y&|4Fs< z9~sg44{G60lFME!8K0mS)rvu+{Ko^OK?VL(VCs~{VJ66jU(kI@PJs^#GGy=@nrQx^ zMZ+IL(L$`Hf1%j`IkkU-rq17J4uWM9ih~y$V25UlU-%LYw&10tz$aLnP#V0rf*fl; lq@|_*sGenpU26~^zyOOTk1o8h`TYO@002ovPDHLkV1k^sy#oLM literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_d.png new file mode 100644 index 0000000000000000000000000000000000000000..204b03d4715a0775947d55c10b1765a4a7a8e1ca GIT binary patch literal 470 zcmV;{0V)28P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzcu7P-RCwC#mR(B3Fc8NlAN{cS=vze; zynzStgr2|)_~u0fMcfCUM3mBQ+Zi)Wy0&XpSDU&Z4wOLJ%&-6XPtpn^g5M4aJBWYi z--GIHA3Qxi=|)w&Qk(6I_4|v(1LgVQhxwbk+v!RZCHr;CvwqY3M5yeRaN@*`UBpUz z=Yv3F;a3`IIlEF%*>(6vIy2{G1q784aD+`+A;P1M(^Unu3cqDi5XLNb8L(5#FCAr7 zL9tTMzC4#fmBt1H!*7G#92w^rmb|r|_P*jIXu`|o8rZ1h#9ebaZehEZqn-{W14QzH zZK=^}E5=Hvc^abyvtllF3h#%{z0@g#pQ7|2sHc|^G9@{0WqAdd!`gwz&1EfJT{s!) z0$H&sUOi}(hR4Ld#nL^ubl@P+HX`BrrE8+P2RP_FN+X-9aY`Ep9(f%6d#dS^eLNTR zVz2dPzXlSsLHjaq@D0E6^CzATPP;BKZ8;wVir<_ch1kU({+j>;0RJp*b0|ngQUCw| M07*qoM6N<$f-TR{CIA2c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_h.png new file mode 100644 index 0000000000000000000000000000000000000000..8864963727f3810a92c5d271345d5d4d726b7990 GIT binary patch literal 469 zcmV;`0V@89P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzcS%G+RCwC#mP<~<Kn#XGk36VYvI{~W zaRUy*2{{25V8=y}K!`57LI@NkO=2_7OiG$gQL0Q}0hXeX+!=rQZI4qyMDWul;TP;5 zI;-h1n=a2U+EwKr)GUt0@b~5Ng|h5)$N%v3I9+L?WOqh+&hGP1gvxe12Tt7DTCB9! z9|Rf;ztTv{*_C?IuERIdl^I(l5R^i|8P-LK2rC_@n+#|b{>r2vOtIW!z)ms0aI`3M zij{)q{jCV9G&UgUk7#>27JR;+^2XM*t1C`|CVX5Dz(yqpZkx+-bKAXa)pRHsAd(Mk zQ;k+zzO8gxO=GlRR?MkR;cNIjOPw<KHA+u{*7P<)rX<&mEbag^7z{jaE?d&ggOj1o zkQJNcRlP>3c}?tFtlhq~0|$XN5ee5XZ4*@;z(MCx8re{_Q(8OlR*i#yPc?mWkLQBk z95vqP_dsGcXqV<ab;B|BJhZ00Y1am(k^Uf1JkY;m;?Mlq{uE#Ury*{0$`W4g00000 LNkvXXu0mjfD)r2D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-horizontal_n.png new file mode 100644 index 0000000000000000000000000000000000000000..5025483b59e0012a754d8556019d97d1cbaf702d GIT binary patch literal 474 zcmV<00VV#4P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzd`Uz>RCwC#R!vL8KoHG-)D)$+QVA+p zP!C@8<VAnI9_+<Gpwb2r3SNs6o9wQcWTwV;Bim%FAP#H>W;gpX^WJ2a8DoN<HXi(f z{ey1Qq|2hC!(rhEQDk1GQ?@+6)9abn*=!~L7*Cpmk_jQHIsgE&*GpBi#oLuvNKv?a zgy`{O>~SxkoO60dRNrNcS>t{(6?Qfl<Nk$odUkHKQi00H@c1NADhr;*<L+V;DgCz$ zaK`fi9sn?7cyyxu1H%Om`h(rXYo=0IwXt1HNt(+@OvI~h1qxYO7It9cLR_DdA^Nm6 zD7J)kaarn&CWVWFxofKsuM)W>n>^fi0r)wN`(8O6-CRQ>h8LGt$=&F7Z)KBGy{a;F zYfv<eKZmp;6i|=G%;&gnj=)|(6@tE@h*ARJCFn*o%`nppfxmF<{X9ib*FLT_=5jHk z^R&{7TUgrn@8WgmX_K3SuHj9)@VWLOPJ+UXjbq#ks9rPPjMG1H8~hMp0G+>T7LgAO Q0000007*qoM6N<$g7NCpr2qf` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_d.png new file mode 100644 index 0000000000000000000000000000000000000000..205af2b98013d8a4fe85502d8a1e159fc7121734 GIT binary patch literal 476 zcmV<20VDp2P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzen~_@RCwC#mQPNFFc8LPii8I-CK?kL zxX{F<mvHCKLwGI^;m%9Ab7`ViFeV74(>jGd>Eq$!k3<v0Buzu1{dK;61E4VmemeyG zK`hV(x<E(8-maS$XXjgECrTP=G!&W}X^=!96+wJ>gnh8**Oz{1S44uv(VZGaP~Rcf zPs{XLqbYcC$*;;B3>3B@C)5F!?{B%yxF7|kvI;0j(^DJ`eGmq#r4a%EWCX&c(K%G3 zn_x=z(-@%Smhq+9mHiNTEh6L=)T>}K>VdPym|;u&ExCt4?PsWTli?9@#Y8(VVQ|<w zTkW1ztuZqesymFK-Q4!Lg-HjVW~A@p3Q+pydP1@CHbXzE)7y`rl11_Pl`Lb41|;_d z_aN{~G;6NP*}M^3zw0{AqGY^#?_xq{ioiF=4T&6d1Lku+t};ZHR%~QCk9%$02l~KW zM|C+>i<9S8nAN>r1g)N&iKxSLv$%I%lcgv7pjY5z_x5b2|Gbu8(0>R06kq^D{HGV^ SWI?L{0000<MNUMnLSTZxjMCBo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_h.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc90375939a789535cb5e4dad3d7a1c22b731d2 GIT binary patch literal 475 zcmV<10VMv3P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzeMv+?RCwC#mO)O1Fc5}kii8I-CK?kL zxX{F<mvHCKLwGI^;m%9Ab7`ViFeV74(>jGd>BGM8K%y~WlBQvx{W^dD0%(kZ-wpwP z5DRpH{$J?cwi_2`=ihQCN*ZZ2RGJ%UkVGIAL3((EL!jr^mtklq62Zdgpgsw-cgXeA zD!tZdDxO^O8><8Zm2Joc1;FzCt+W+aq@Y$-0R?$}ild<q!eBEsLIi+}K)5uzgeJQQ zrffe?0cvgqU#g+%hvc=0m|N7&f?d`F7o92Np7=qzhoJUrP`knKh`3^+9g{IQ?(MC1 z&#GR|jD_mJO0^r;&s&&u;AuwM7FU4MH`f!2mA4rB(K)^Sh#FZG?_bR_)@VR-UvUot zzeKa<YMjjrvFCSvj*BE4@7}wV(3v9g&2dAb1l@sopO2dgk);(ImCoZ{8~2euaMwwD zoSMnWb1Tg1UN53vPtHUXFx@QfU7yL?lYQq`;3W4BEM}*`<!^9-&Jmvi3;?p@rx%+R RfJy)W002ovPDHLkV1m0P({BI( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/distribute-vertical_n.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4de0f1a443e1e9b66da9e20845cd99fb65622f GIT binary patch literal 462 zcmV;<0WtoGP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUza7jc#RCwC#R>4ZcKoFfYmKG15iXH^f z;=zL+L@bKprGB<vL{S7mQN)9wP@y0wLQz7xGixSJ=#ZqQi9r$aV3}rjhqrI`%~Ho& z3%`vT`~fP`iuC`IPK{ewUR+AdEwtN~hyV=&q5(nzV+_2#zBpM#_m2<Rq-k(n-fmQu z34VqjjbKnPP+~Eq8(Z6!5Wyg;LLi9|nI>+{&UviZS^|FIc;EYA;N&K~I6HOZ&fVzl zS60`&(T)-EbH6tqgkdl~z82tH^;zX*;<^w+&2?kTT5))=dd!@u<<26tq9Cm6?bT&d za;AkqgJiFX&Dp%9lyXv%JW9$2Ux<qtX&lx!JH9s8-OY7V9R`2Yr+eM~uq@n{dN7$z zVsr5vyEp7z>Er%WFTZ<_4i6IUWps9SJ)Icbb4wKZF{S0WmvMe_JR{!?6;ZhrN_DS( zoMJIeX{qjwdUB}BKJgP+$h{ednFvnxZ?GaQ1D^s602>{<G=)A#UjP6A07*qoM6N<$ Ef|b6=EC2ui literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a368096d8591609dfbe10485936fe30546d0b215 GIT binary patch literal 552 zcmV+@0@wYCP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz%1J~)RCwCtRy#|>P!vA7ui#*z{~%B& zvEbk+IJDquNB@Mgi%@WJcIaPltgEzG1eb!dbEdjz>jSKPr1hLrBO<lEw{5ccg^&c2 zeD^z#?;cgDRJH-2ie~@`!1FwiQi4(nj5eV*x}=D3%{M_Vw*lE~7K9Lx6x!{l;IZAQ z5-AgI<@2zf$?%Gj!f-f*daV|)EK-;>A%Xs{wFV}okS0wS!}vZ3NEsT%S(6q|ZdOyx z7**jy7DW+1({&llC9R+CeG~7OZ{hNI2m1X146qUxC0DK?Jvh6w{y8n!nUp>vHKZ`c z#nI+WqGq!Rg+h_<yWNkk&XDX%*5Qpq8WSF#-_KR}-hD^HF7*06Ov3<1<FQ*#ugbe1 zg|t>mpqxje5hzrr6`BG1uXT6{Y}R`0UZH}FUV-l$_v+=Beywy_SdsoLg49S?jsHXH zw4YJqr%p;vLyb>;m>MZ>O?c3TPklJA;{Ter&@}AQN<Pe9itihvx$$S4!^Cr^k1bL; zxXfg%A#0oFG}ht8In-gWlB{%DhZ4T=!_ISu$3q)uq8VpVhk9tW=n|q*Q+j!Qt2P?f qfz}46aLgCy!b_q>Lp)2L0t^7>^?jA+sQnHA0000<MNUMnLSTZCZ~tom literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_h.png new file mode 100644 index 0000000000000000000000000000000000000000..ad60be2bf5c624e4530cfddf9aa76d14d6401f97 GIT binary patch literal 773 zcmV+g1N!`lP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!r%6OXRCwCFRzYhMQ4oIb?c2>E5LPR! zH^H{vgkJmsp46L=OVF$6fAC<8lwQS?=tU5bg7oCcA0X|iO)g&AdkqLl=&o+;z8Pos zC6XpD$)+?N7<Th^cfL0>-+Xzxd2n!D2vH*s#qzC7zO5NBilXpZ6fGU`SGygy+wGbL zgz(DBia?Vs9h7m7e;$V#Q3zv<z)PqtkTV{Eq5v>LNVR-W1i^R;0R$KZhN=Gg<M;Zk z=Y^ec7*E*+BeE=;@Hd}!>wINP)4t^2e09BB#2JV2<lf%y%k{POsU>;)WxqbWe{^qc z%lCa?mr@Gtc?(nF@aRxzMmCH-_VC?Kvp)X8=Wj>Piw1vI-Dr%j@eUb}Kv5V<op(NJ zPafWG5Ub^fuRmT4*Pd24W&*WZEwOrYb(8N;PGXM?wf4{(RK$`tClcnF^4N?!16J?e z*ct-9-)YXxu+!@X-Ci$<lSKbcQ!g8hNjQv0;$U({xtjC%{;gN1r+-z^OxfZ%(WM|T zrBVRC)`l|!2_s`;XijM{Gel!K8GzQfG_cStGC!ouzw@>Ime(wxu$da*OeyuLLCiwz z!~rpfB^@`$vqW;RcW6>bTugPW0OVx_aIhCy23aXs2Ia*_-G2qL#5s#f8zQ{Cr;IKx zNb>QF?1AGsT5(vkZ}T#>)pN(S;=nK9{49C!i-8hHj3tvRF68CK$%ebEQmJf`$^>b@ zujiI{&^uRLerC1Tn1W?P7I~vQHM2B4qdcpwxOsWGu{bBw2Y0SE{-k|RDFs2`gXd|O z4N9BJlbuE+E^mRJDm73U-#7f%R&-`vYg$aH{e(zRxkyj5QCU0ZOj$)L7z9WAa{iIx zOz8j|$8lILmzRDpOOhl%a5H<iyPZyr{L)VN{{$ES1{Qk;Lfc;E00000NkvXXu0mjf D>~Cem literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/edgesnap_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b39ed929b4d77b672d29ac65c038f521d1232213 GIT binary patch literal 361 zcmV-v0ha!WP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz3rR#lRCwBA{Qv(y1HAzg!vKI$OKA#N za7m~rD8kvP**X7bPo2U@iA8rF+$YYbAj^<L1<I#5bQp1E8JJIzEe86Q8%X^H;y*wP z68}ezPjOn>+1*P~21bt#qAZ2_6cnU?VPdf{(ReM|vv(guQ%yAolF&bB$owNEPX9uK z6l~$*B}<9%Bo7dS^nfg91$m65ECaNV8R}A^d<q5ZK+Ft{_utUCCoan%1<xNcqVyk< zrDPQj{|BN(`Zo}kju!aTFYvKte7ai-Eope5`H()AB4wZ7M5O3H_|y=rWzb3*4yb{D zh{$Gtku63pds#><Wqu=Jx&$xG<^Sm!r&KGM7)MNF8z8^{y)bG%?}bz)00000NkvXX Hu0mjfu|JOG literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_d.png new file mode 100644 index 0000000000000000000000000000000000000000..0aaa49872378530cbb97a771f14139155a488c4f GIT binary patch literal 864 zcmV-m1E2hfP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#0!c(cRCwCFmRm?vK^VvX=Uxw)tE9bD z$IH6NDj9|LAasEWgTSR;LV~b|BD#<ui%%6s5Y<bRU5Jo`E(k4I6r`?#m(nch;)<EE zQLIR|n-;jc?K#_-S+$P3dURVq7|xk*hWX8ZX1<w^sj8~V1K`k#j5rv>FyJ^2JkLWE zM7JOcHl6^NN}`;RkqMK@1eRqHFY|i8%fnuugFq=TCpQ<%5)$Z);$@*w2o3f1GL=OX z#v4eCj4ucRD3mN|ynz!YN)lKsvI&~FMTatERbw5*ai<QQZC&W^cf;=w;NsOvSgcvI z@9lgcL!Kc#d*npdi*EG1|3H1#95ae{7s0wN8=pVAA!o|k8R2Mr#ss}~;U?NzIxy(@ zhD1{WGR-oMlpR3v-d$*IXvMp37j9jyp?wRsY=o$akR)ctxHk-SY6F|iX*gPb5c!20 z;Cj=G=gl2BQEEpZ7}WI*27iNPw7nDf*q|oUa;osF`wMDo92gz_rE8ZY5rIGit|$t< z2$YaQPwP}Oe0s?MeZ2#C@~{y;?+C3=l)_E<ICZ{avK|hHp@voZoym+tRaI9q%xh3m zx`+PnJ5++C6-kqAbKXowp{lCr>J?|p>9_jEUDVxwj2376v_8?yxKc&cm4E%iL&LCF zmQ8DmU8$l(^_L#oKWm=_Lqky%O!x=-K1J=bU}&^uBmOh?bLf8EWBiF*=<-%yxsB$= zcItoi^6lSvT_XNRrBg!TLG4prJ<F1YoV;x8ux;1nM=?PymUOsW{W@Q2YBJUrti{%y zn~|7oq7Y{M`68EnR4B(8i<<J&$6-Hw5@UWpvaMzm*|uR-`szsB5f}wUU_8zK1p3m< zoVIYr#HOrF!S#EWH0T%t+PkT0R~&({1jI&COpZKf^gp69T4u>>dFVV6D~{x@;|EKn zstAZoJOXD8rLeptN^ai|pG;nbxso$7pr}ofz{q7j9}uxYkB5BRno`j=GWyfe)Z~-} qO@*kXh#cJuCLSr0TPwCd0t^758Y;|RZHoW^0000<MNUMnLSTZZ5|1JP literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_h.png new file mode 100644 index 0000000000000000000000000000000000000000..aeb7e4b5ea74febe766c753355dbf439ce936d65 GIT binary patch literal 1074 zcmV-21kL-2P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#)Ja4^RCwCFmSJd9R}{z3yYIg4)vjq` zyQY4!txoG?sUTy5V?%IE=$uY)_QPN>5S0yvpZzihV=(q<;0O0%pT^d0IuH~Uw{L@r z3hgG*cBa*d*oK)JB#BLY_gv4pFL_JiOPUShg+pF$?mNGnbN=_-YfVp04FJG6Srpno zvSbsIf$?}e+82+vop?W;j;7P;afuqEvEE+L%4<86ISY#mQK>b6QVR4l$SsmFkDwY5 z6@Vq$57h|FlNu3F5nf>Xp{Rj*5*bR7;{Ra&_}$5o*&DMPGnog2%JbYqA0IsN@|Mln z=3s&n8&r$L^u0y*!r5O(=2G(;GxswiJon;;^|1Z*?ew`WJ9B+zXx)b4Kctk}mUgI2 znCC}ILb95`P8|Ps+k9$%w2&_hbVs^jaA*+T-a7`n#@>W0lUHGO<}Wxo@eA+scM@9@ z*H^9T^g3NvS(a4?EJw<Drqn?Fg6eVi@4IgeuOD6>+r106Y#oK0vniOmG7X;{`y5Ke zQq9^zp<t6QZ3}@MaShan)(FW@Z?j0Fu^#B_?R6LKEy9`8=OCZU)%@Pk@f?&&Me|%N z7KISDaHJFjSM`xHkD7xrifXn`m8W3t=A5zmvF){2VaLul;EPYcG~XY4o+Z7KrHnW+ zWZNhZO!gFlui|iH2i3NAXf3?Ee~)?I_ugKJt&Ro1@p|?d=7%{ZQeLR}g(i{m!s_wS z(ZlBR=O0eN#dE*G<;lyz8g2e0nl8uoRMv<kuv}B_7LCj!p;cRD@bgk;366YnDEJMv z-vEqg>iigv5t8<|3^Y+MHrU5Jf8hQ7HEV*Bm`Lm1<3FU)S1+2>G?r4M=24lyed|u} ztgGv&Ftk)b8*JLTlxjHt^;gFsc`aEJy`Q~szkYKt`EGu&)u-vF?@z(x@0ZN{pVQZZ zdE2&&MkA_RzYxF7^E;ASA=0|^o<DP;=6j?klIkCLVQ%!5M3Q@}*1m=LEelCE6w1F& zyJsFOWk;&#+)nqO{x$u{&52FP=ObN3%M!XAttLa#r4I8OoTqw)^zPm_cAAc{7;OnR zwmO=6DY0p)FW&dC0ucn1(nje>ls1{;5Y5tIe*N>bg-Cm1k>wAMeDF=akaw0JEmK25 ziyFG7CQC<{M907gDjl?Kp}FDn4h(ODm77Bg=P5bD4yVJ@zECU%5tM)qT7pj%TUWMv zrKaKC4O^>eWzgY)uIoPLcLae1rPc~+g`iuw-I<#51vr<>MZ3DXR)$jAz_xTvhxrfJ sn|wZRF5FzxiEK7IPWH@h_)i2F0MvWn(vnyQfdBvi07*qoM6N<$f*(;9RR910 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/gui-library_n.png new file mode 100644 index 0000000000000000000000000000000000000000..abeaba153ac2a6becec702a14031df7e9e527813 GIT binary patch literal 665 zcmV;K0%rY*P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!J4r-ARCwBA{Qv(y1HAzg!vKI$ONXDO zEW{{MQBcGxZs+V00HpkZ*a3(IfY|WR-aQYH)!cb-pFB&EECCq|vnT-UAXPO6Jv}`J zMMe3C8#iup23ooZDEt?Q|54;qpk)O>DjtXp`1tr4<Q3!@3=It#9PDfuE?v3GaP#JE zhV|<=$^+%+0P$)d{t3jtfcQVzmI??6_De}ieem+~WYE%3XL$7Z3B&md7a2Ocdl`QI z{E4od9qP+JP#=?LDIY%{gNTTzz{{7f7`AQO&hX{S7ZiCmHg<+zzkVW{$qM!9U$Rs0 z-@ktu?%ut_uy5Z!hL0aVg849PYHG@mo1F>g0|6t{Tt<qr%)h_?P_Vqb0z+V60GRFV z>CV8*%LA9AT9*0uAJwpwq<ApBdi5HHLr0D=oH}(1Lk-1+!=FEYP%yIiyLayy(o&K! z<S6&)zkfKz1N^-4sv+Cb|Ns8u6@UKXB~CSDTl)9kU!3AIXU}7}cI_(l%ih0#P%YiG zWgEkZ6DPppmoHs{i!(AZexhFRqNU88J9eNdU}W6E#K^RB`=$-6khM|f)4%^V{Ac(d zfRgMPnXWQ1F|L09{{6~RM~{4hTJj&M2>ef}PZNO+I7MLa2?Bk!6Bw*>_wL^I7?y~k z_%GBl5dMv;2m}WVF|9#peF}0Q7Z7to^Eb?*zfep5An{?orno-+3$3z1fEmeGe~^6p zpK1vSDPh2ZjgeG~$Zk0xSws<F9EcV^K!5=NOtBqlf<Ao_00000NkvXXu0mjfUn4U) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_d.png new file mode 100644 index 0000000000000000000000000000000000000000..554647640c1509957c5db386fff6bb6fb61d011e GIT binary patch literal 385 zcmV-{0e=38P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzBS}O-RCwC#md%cWFc8P56sdYJ9*mmc z!A*Pv-^Q!Y<&$Zm2R(RPhzcw%PGulU@T1YVm;RHc4Ab`4nU)roWf^>14*Y{G)Iu%P zLTlwda!$rSH}(|I%m5SwQbH(GIEpqifvq;{=g_J;3=^Y|YM$Fi03pNQD2!t`#0mKR zh`k-@{KU)K;Dn%qHu)7eO`_0il<jxB?al3RZxRooi<V@x?i%6D5*}R~1YWw_A0P|7 zynVOV?_njeBvcD5ik{U=P?84gaCl1=CV`Q>cfBFFUdqK~EI|OM)avC(NjhbJx-&-J zq&{t$2+)8j4bCf(7XHi>swy*8)USA_ff3nGT{?}{^p0e{am#P~DdZDSWdf+(B=#T4 fLM_xpX8{HPmndRu8SfR>00000NkvXXu0mjfV5Xy; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_h.png new file mode 100644 index 0000000000000000000000000000000000000000..e02673324fa50b4ff7a37fdb6bc882ef38613c79 GIT binary patch literal 385 zcmV-{0e=38P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzBS}O-RCwC#md%cWFc8P56sdYJ9*k~+ zhuvh~VBf~8&*hV8q6a;AT#0-wEjyKgXd?<SQ4jqm%`kM@U#GMaSf1zbY&q}(S*V3t z==($6x{mM9uM7VNOV<Gu9HfLmW)O-#YXe$s*7u>N1`IbvS4>{`A%Kv<ZyY2E9Fr7$ zf65-tbRO}lC`Sl7YL}mZvosF8P8Gk~Z7+)Wtwq#A7d^@7n`?wKOL=_iAn?-V{sdXz z74gH-c!Wk`OUVjsik?*?C`p5LI=m-Km%vEgyWR)5UdF{~9zg&wtZJMoNpIPo9*miH zDZWn&0h%zQ$+?kf*`K$CDr2RJ#+2_gFecxr+s?L7Q?fX?)%|V+c@(PJ0<Inu`@3SH f7W($ke*p#nj|*aJb_eMP00000NkvXXu0mjfr8A)^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/horizontal-center_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0233a804c513e3f663eac98d836ed25945f95027 GIT binary patch literal 374 zcmV-+0g3*JP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7)eAyRCwBA{Qv(y1HAzg!vKI$OGhmo zwRG@X%7RNmML`jTucdGB|M#z73_pMVVEFy(H^cKMj~Pj^=+1-tge^q}>}+fpLLy?~ zKYsuI#qj6PABL}=KQmJ7Q#)rDc%ck36oiq1oRZQHkdI-2o0k`*5Xa{ToR%KiyN3}P z=gc75%HH7zvi$Aq*Q9|KKZWr>zJD(Qr2Yc&FDT6p#Q&h|e?(ad1t5!AK^C*HU@Cod z@16uGAfV>3LoHxHqyG`@Qz!rh87K_@0)5T_RnG-=2m{o>KTyM&(R>P%|4*u=|Il3e z52^<y2D0!6l+T1KI7zh>9e@n{hLpdFvV>Ag|B-3|)`T+JTp6`=)Y3t3DL{Y$05wcz U(RMtuhX4Qo07*qoM6N<$g2x-3#{d8T literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4c405c57e9c0be7241c5983b4e33cc8d63ff8ce3 GIT binary patch literal 325 zcmV-L0lNN)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy=Sf6CRCwBA{Qv(y1HAzg!vKI$OGhmo zx|XsKqbMycjp)q0Z`W>aAoUZ7|K53UpL|O}Kw3tcpqi&oo`NW0AbtzP-zf4aDWHjg z`H?Uq6GQg@-;62>ig0mcUsF9!RaY#9G1-Bb6`LKjOh`OH%mu{EG!I@Rz>dpfqlLo= zEs>BiKK(3(7Hz_yjE|HLvF8(RE`|?O@+qh+eG4jS-bKdZRS2?>je(IWmi`5mrJ$k> zn*Vr^@*!=47glaU%hG>9{D(;M|3KAKTq6C2)*!^B{C`mO6kGZao9jsdj3cJ84G>@e XI=efQjcj*y00000NkvXXu0mjfl!}Ac literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d25111b61bae06e365e5a20a36f2e7013b75f338 GIT binary patch literal 326 zcmV-M0lEH(P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy=t)FDRCwBA{Qv(y1HAzg!vKI$OGhoG z3Sc3mAT2G8=sdM=*KTeg^%IEy-g$7Jd`m$<T1J|nnx{{mf+%4iehb9kDDo*OpoxL` zkuW0@L-zmQj4BF>aB*~BQ#DRiS1g4w*@2i9m-)0xNIXEy1;orW4_+j|j?Yu0g~JFe zk&rSz{VatRZNi|8kCYFw=M!!&h7VNoDX1)c3o2>eMaJS)2(plkfsrbf{son#prQ?$ z|9Ft{A#H*eR&GMe(tkkwhe)&kK-E)RBK?KdAjG8le^B)lTlx=|>q!BOqwQW=0)PMm Y0M%JLlk2Sf0{{R307*qoM6N<$f}00~hX4Qo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/send-to-back_n.png new file mode 100644 index 0000000000000000000000000000000000000000..70721965f42973925bb7831c983962dbfe8e317d GIT binary patch literal 328 zcmV-O0k{5%P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy>PbXFRCwBA{Qv(y1HAzg!vKI$OGhoG z3Sc3mprW#d=sb1o$YE|E^%IEy9y)xGd`m$<N?MAbnrBa+fhb`hehb9kDDo*Oz{te( z5f?WX!;ymr8SQNC;o|7NrfQrfB`3m|>_E(l%Y0fTBpx8<0%B&G2QLy}$LFch!eIoK zNJtr<ewIRuHepc4N6Lp-0T(A110y37ReTC6OW%S@niVTn;#CN;keQj8Dwh5Qm8GDf z4VwRWkn$mIf)`e9Ld(*BK>UYDv;RQVQ(Pkbh1MX%r22nQ^%PtB50~pn0gR*VURnZx a00RJ)%{i0gWn@nP0000<MNUMnLSTZM{)1)! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e97dd429dbf85ee77126c83c1f3e657afc00b550 GIT binary patch literal 653 zcmV;80&@L{P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!FG)l}RCwCNR!wLVK^T4eGqWkOr4gkS zQ5V}NND2nMNh#vhgDIXoNbRvg(o+P%t6n@rgd8f^TfrjaBpzBVo=jA#5`?CpU7|LH z+N5gKR-@z0V1np2R=2Z0_*lNT%)I^HH~TSKCX+b`Aj`vn0%)2B!!Tf)CMcmUp(Jh! z!8VIJ-Q7J1hr`fy9jZ{NEIV&1)vOSeg3;by*zN7|i&UXruVZR*(vf9}3RMdVi2Vs6 zkfOp;)q)LB%Yu%8L3nW+5*<m7w9L;Bp2F25F<jc)BV%mf7~K1JyiYTC)pw=K#iwu{ z%%H7Y!**ygzCJ18-f#jRmtK3W@h$p%^fEd>eZ!t;CkAfc#PJ)W=<1;8Sos2X;+pq? zT5q#+sT9`MKL63;qlF^2F<>@i<5p%&a>FP3v2=Y3*6IiE&G|%P&k6<B3duxr-Mo}I z$!2#)*kU@><jNP%{>C+s%jKAFd&B$rBx4Mr(tMK(&A*V`7NV=^Dc!LPCbw6w)ta24 zL2}}qlAb!N6`bbJ#m6sk;Rm;~%J6EdhQUx5Vk&w%^Y9U~ZCgeI`yvsUPmGUy_SE<6 ziyc0Mp)*5BUr9H);@n)zHGz*oIhBOH{e68n5RJC%>EGbzs((d3pZDKWue=pD<v;xw z{1?#;*}IvL_8La;#p<SE1~1C*p}~t<)N-pURe3#Bjq<{Rc(^8^H2(J8`|R}eU560L nZ{Y-nX^6R@Lf7RH|1Q7)A!IJN8%6_M00000NkvXXu0mjfmY6pz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fa681e049dfaf63115f213c24828868e0b52ce30 GIT binary patch literal 904 zcmV;319$w1P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#DoI2^RCwCFmQhGkQ5b;#yWP#%u<mko zt(H#GvX`(P(w?N33@UvvQ$eRiBv~K?=7Z3-NAt;tj37`SB3Dqdhft_gBG;Qj5>bMi z7<$X(irZYddrs%vH8<zIn?7tm9Jv2~?!Djt|K~sdIn4CbR2~4-lfgmyMI>X0r9htN zIWy0vowzIroFEAGEW(I0<>msG!fA(6&f?M{$6^d1NfO`;<Q9=Zc?609Aqjxdq#ueA zP#%jB0wfW<1}VPS^RUO>algZ<sz2zmgWwfIp^#jNoN2$eqPk}E)cK1`+n@*o<*|rD zNu&s=dl(Lf74;uqrn#)2K41WG27^8_x$d`qc=@c^a{OfPUj9gEI~ak6ut6dZYJ-ZJ z{`t+T3Qn(u114^%`p&)H3yn9PaC@>tIjqJoGxEAr>3@{ROiq4u6crT{Z)4Ke_qdZ3 zmm$>}9bKJ=?2UC;G3LT@wpq578~=V<@y(SVEIu)Dy7BIK{QI<6XJ^0D^pZ$Px)>&g zZEd~P9p`|(t?jl`NuPPzT^IZvE-ADgnmAT-QwDr{{-`pXr30g0C+~_aP`PO3LW-o9 zNfJ@w$5EX#n+5HfSfcT-@==>MxR#frXCX_=Y=_c8p$JQIMJ3y(D51)tx-Ms*s3G3s z@P;93qs~(sokI%MNqNlp*qEchS}4C5mHz&LPVzy@H8tTsaAre*Aj5)3cUZQCykEU* ztD?2yQq|zO%FEuZKmeb24p<4%NF|6*gw58pxuydHgZ7q|W~Y+w@p!oFU9NWe{Pz7j z*;aL>sy!G80AKnG&L*TcQu)&VLZWNRB=00jdRcjy*l4qXt+~k=wGF$6d)2<9Eih6@ zn06%E2AL#<gm$H?3ZYUSGdeQj$j>j>GUsx+cC@BS($LW0Og2A1?~Ap;c&V+u+8uM? zg}tusnscXm;=h!30zFSj?s1CK0q;srb<#u%NlNaWT60j-lSfK=uiNc*SS%LElq@VP zM9x!;amJvL^HgraC}U8xd2`4hRE`v9I8ynJL@&U8zn?Q2jcEr4NKd9bx~4;-DAtpa eu^awB0R{jK4i)x⁡&r0000<MNUMnLSTXsQm_U9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/snap-grid_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ca97f4e92cc19dc9f8aabc3c8b641fdb8a5805af GIT binary patch literal 683 zcmV;c0#yBpP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!O-V#SRCwBA{Qv(y1HAzg!vKI$OKA#N zunDRtD8kvXNh$vq&zr-Diysmd^Z&*3XAgi0QUUA>CZ+?7OiYD`_UyiZZtk52_et_8 zNF6hhdNAK7Fj(o!=g$n>JUj|Kyu1t`%*D;^z{$zEKOi(*1ef{5S_-m|9ZJLbKv2)c z&BegQ#dV33gVTtIo5u;r{>aVEEx^LcngUeAhRqJF!3zw+cR)%IiT?{t9)<s(;lJMR z-+#{i|NoDHi=BHW!~g&3j7*HeKq*eJGy}sgD2>}vV4MlTf)Hc@kk12TvjXvNApZ-D zpPrky2uS-fGBWnem^5)Fko_|wFTV;@-~h#7=7aEWf|f!7Ba%ETQqaQr92}h2K$Zfr z9xNEy**SE;z6W_5Vi6P6ECymM1!?#LW&g%T1ATKJ$_M!zWXW%!7|cfxary>Z+1uFE ziV~+d^BXG*13$k2!{5Ju8D717$pDN;hDQ${9t2u!56n8ghxhG02~_q2h`#~xUs#r* zC{6`MM5O-y`EwPd=hKG|DnRNxpMbz|NPcBzgof5X<oG0%Ww6C5D>DE6n>Ww=f`UO7 zO2M)bkZlCS0w3PJJ&qJj3{;C#a2!I?$WN$4enHuPk<9svZZSo1ij+?oVCF2HKaUw| z2{Sab{z8KoUJPQj6k7HYj8klUXz2VywhX=Or6`}`;4@+K9aZBL3;hRMiX|AQ$W<E@ zutN)PSVAGh#|Tn#Y8mu6BE%=X62}7mq6HZtK2<FJM<O3DU>t4t(h>j!7yw?P4^NGo RT!jDt002ovPDHLkV1gLkDZ>B& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_d.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_d.png new file mode 100644 index 0000000000000000000000000000000000000000..9b580ad5b7048ed9e9ad7707cedd094dbde57073 GIT binary patch literal 393 zcmV;40e1e0P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzD@jB_RCwC#meFd$FcgMQ5^b=eAb8hZ zOxT`h53%RktuN(ZUg(9xi_wef)+T3(J!Nt9!W5H&c%Y<|o_?JF&mYPV5n>mI9_kLg zJ?Ox<;puU4TDRK`X4wO4d*^-qab0MOQWk7;r(?-0%q3|J@99`3O~3H{PA6#kUEoAY z_}YISqiiHw6JgqtGl`pNbuj2ukc9?Z)SlWZFNJoJcf|y=;6kSJf0Krh_eF8c1ww8Z zmfQm`8&GoQ$KL&u8F1HtQbni+TX9lz99Z|Fz?od*v1Z(Hdg&{i<ukYGHByl<nE zzVh`Tj>|-6(Q@iYSUNsdL33(K(Q0A}LxTS}&x1}{(WDiD!yfqLZ7+Wgnk3aRHj?YD nh9<3%pt?&F>*EjqSAYQk-p*10EL5BI00000NkvXXu0mjfw4|>0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_h.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_h.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d56114280ab75a43b4ebf463855269f94c801b GIT binary patch literal 391 zcmV;20eJq2P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzDM>^@RCwC#mceeqFbsyB1R4PXY0_@x z0Mhn6dx$;Pu6wB=!C@RgJ)j;6j3!<}T`ik>37W$JELEwhxS#z0b`*h#kUAt>A?{Gt z=*4V)n$6Gadi{xcJ{4Vi7OUm|LOWFQVBh{WmAuM5npS>KM>=bB)g}LkP8c359BU1` zVtYu^NIu5OrDVS(ZmYFy(1{`o9k^;d^>K=UM$7vll<?q64<G+j$|LWKkyt8((lMUV z6s0k+>17YGBNGK389EP?Y^KxIxM|*GjIUA9fdM)X0`N~;Sr%v~7_d(;$s+ID;H<BH z-K)cUX7glo8c28we5_gL)P<qdjVbgA{uO*@t+b{}YXb8g_~h+XZVr|twJ91Y^wvT< lt%;!a(nN7Hm+fx>1^^R{QUGJ8Unl?o002ovPDHLkV1l*1q&WZp literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_n.png b/Templates/BaseGame/game/tools/gui/images/GUI-editor/vertical-center_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d0f6c45e52462824e70fcdbe4b40c53f24b96f6c GIT binary patch literal 373 zcmV-*0gC>KP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7fD1xRCwBA{Qv(y1HAzg!vKI$OKA#N zNKve!pa`b9d3pbT{rs5`o0>Zh?o-!NETIIl7?k|~(JOeFftVdi(>_jFSXf|snaMGO zWDSxsvj16G*%<f*1%J>bA%OrZ8!LJc!+iUXW|p$EbD(gc^lwruB{_I+U%$o(viSLv z$0BGxrA_dfS=;>o{p%OQPasy+)Oz#t=MRQQ_wEUjnq^qXNVVwTKh&p0$161~WrNcH zXksZ2i&>$TG0`M=5AEFpD)!jzoLzn#J9t2vZut}`2!A8R;~$#lQy};YrN2Rg7G(J^ zBypPh^bgdhf1noAw!nwR?SQl#{y~HEA3YKha@_ud77lc?^dGqz8AnWG8z8^{;9^Xe TNxl9p00000NkvXXu0mjfA620a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/NESW.png b/Templates/BaseGame/game/tools/gui/images/NESW.png new file mode 100644 index 0000000000000000000000000000000000000000..2f73696c83e32ff6d2d51404f9d28e926138608c GIT binary patch literal 3092 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/NWSE.png b/Templates/BaseGame/game/tools/gui/images/NWSE.png new file mode 100644 index 0000000000000000000000000000000000000000..c952a3e455666d69ec70f158090daaaac5f25a17 GIT binary patch literal 3101 zcmV+&4C3>NP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006O% z3;baP00001b5ch_0olnce*gdqO=&|zP*7-ZbZ>KLZ*U+<Lqi}?a&Km7Y-Iodc$}S6 zcUY767CztiWe-+D*zmEJY=HnGBdiF>5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1<R zh~l6qxMx9%h+2zPTsZC@+^4mDdhhM+``7!t=bY#K&Uw!dfDsZVk>;Xm069{HJUZAP zk55R%$-RIA6-eL&AQ0xu!e<4=008g<d3b(wus{3(uWtYX0C3eVBofEr|AV?vCRYF; zkpSQ#66Xs6kWv81E>y@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z z9H|HZjR63eC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoX znL;eg03bL507D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVp zu|i=NDG+7}<RYAxn<EoQ=L1a63;+Nc`O(4tI6si*=H%h#X6J10^u?n7Yw&L(J|Xen z{=AF=1OO0D&+pn_<>l4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04 zJRKYg3k&TfVxhe-<BLB3GvROGi+=X}Kpy_vdhh^onn0PYz@vlxaba$Du2PQY%LGC( zZujRS{>O!X{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^j zz)rRYwaaY4e(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJ zT&R>6OvVTR07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#<bWIsp%|7y8C1YJ z*aWq(0~(+an&A+%!7(@u=im}tf$MM=24EPT!Wg`U2?RmN2oqr;I*1Wsj@Tm32p5@- z1R`NbG?IX%AnAw{Q6k02a-;&OLTZs+NF(wsauhj@TtNDe+sGg?iu{VaM=_LvvQY!n z0(C&Ss2>`N#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;F ziC7vY#};GdST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_ z2v-S%gfYS=B9o|3v?Y2H`NVi)I<b&gMyw|8As!)~C0-{E6JL`^Bo4`v<W349C6F>n z3rTB8+ej^>Q=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJ zloCocWk2NvrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&G<BLK&6^fO%cL!%)zF%0 zXKD9nFX?o;3EhJpMVHW*(rf4k>F4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A z$W$=bG8>s^m=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn> zP~)iy)E2ANsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB` zSVGovRs-uSYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^#<Ae=IoX^_&LPeX z&U-BbEk7->)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#i zb<gTP(_`y-=?V49^$zLX(MR=d^rQ6`>hIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}Hnw zgyE<W%V@fh#Au_@NuwvYChmu4<285}K4z?M9Ad0A-euftJYiyKGTWrYq{ZaEDb18? znr6Duw9|CV%*ZU<tk|r{?2b9roNJz8zS+Fn{EdaBMV!S-i#ChLmfDtl%LSHAmiMff zRz6mFR`pibtVz~f>n!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|> z><a9f>;~;Q_F?uV_HFjh9n2gO9o9Q^JA86<b<B2baJ=iJ;WWdk#HqvSS7#e%p>v({ zH5aB!kjoO6c9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6 zJ?}yE@b_5aam?eLr<<q3^N{B+UUpttUi-ZsPqUmRp4KpJ$lJtQ;JwRxU^+fMW%|zP z13tz+0-t)HhrXu1BHul}BYxI?nSKZSp8Grc%l(h|zu|fE7V%C6U;)7a<pI5c8iBI| zYXctynFOT=H3f|Yy9O@|J{3X?2@P2va+7bs7xEkVV>8@mESk|3$_SkmS{wQ>%qC18 z))9_|&j{ZTes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^H<b zj`5GFjJZ48YPNEAXRK;$Qfy=Fo4A0us<?r8hxkSDmlAXnBnj<_<iyy-J&EIU0_SX+ zGo0j_RF-sOuI1dKxfkZ?&dZ*6JXtkakbF3Wm=c$=KjniULQpRlPvxg>O&t^Rgqwv= zMZThqqEWH8xJo>d=ABlR_Bh=;eM9<ahEGOy#xn^|QY(3p8Irjp^G#Mn*50ho*>Tw| zIh34~oTE|=X_mAr*D$vzw@+p(E0Yc6dFE}(8<U61_v9n_bMxC3Y=unGqqI`4P!1MM zFQ_YcTNqn-xJbQ7TGTV&X8!8=BMX8Se7%scP`I$O*tmFE@!%rAMY|Rwi&GbOE-_tF zx@351@X~$DXv?ye{ZQgqQdRP5dED}jQiIZ^r9&%%S2UHWl*!9(uJl^DV-;bQWL58K zm(^QVe<~N1U#xJfsIK_1M!4qUS59BmeD!&4+S=Yqx61A7Nb98QZmjoNzpqNYYC+Y| zhVTuo8}W_h8((co-gKdQYW0rIw9U%R12tha?OV*YtlRRTHly}>oqt`+R{gE3x4zjX z+Sb3_cYE^=gB=w+-tUy`ytONMS8KgRef4hA?t<Nq8e$u|zvh13xJP$S#h#CQrF#eV zMeplsbZ>0jufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp z&EJ`SxAh3?NO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j<Jb;mW2SDv7qC_VA{ z<bspqr(~y|olZYJ)S29Q_e}hmYh6)Yy=Ozuo<A3K?o78|_sR3#=Z{_Rym0g)_hQ>6 zw@a-(u02P7aQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI z-5j_jy7l;W_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBk zl>gI*;nGLUN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd z`HRoLu6e2Ra__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLL zKIeS?{4e)}^ZPO?Jahm603c&XQcVB=dL{q>fP(-4`Tzg`fa(AMbua(`>R|u?I+*|f z7jUf20H6Q>010qNS#tmY3labT3lag+-G2N4000McNliru)d&g`4<?!)2!a3r02FjZ zSae2dY-J!$VQpmqRc>@?bZlj0Ei+!Opd$bP02p*dSaefwW^{L9a%BKPWN%_+AW&#; zbZ>KLZ*Vlrj%NS>03mcmSaer%X>?_B08@2vWpYqXM<8N(AVP9wZe(F@AVP0!Y-MwF z`}WZQ005~;L_t(|oRyMM4ul{KM5i0?1vjJItekB2!>s|O$WCHHE%O|TqN+q7RRu|j z`T%eMKq&>Swbi~*A77-DhDOf$_3HzmhwPdk05_W%L?oO|ucI$S1mx*+$Xk*t!OtwU zRsiR5c9ReQNx~mhrOV8weSe3(u>(=np$-8vh?{%B;}&|AQm&bp8FJ2(0W1<kNw$x0 rH_vABx7p<YA>(U)`v*wM60oB$)BjXnmq{tO00000NkvXXu0mjfmLte! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_d.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a9417b4ffb41cd0ba6993cd45f2698840ca319 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xrg*wIhE&{2`t$$4J+o><XQR^rCZ%(03yweS;XXETH_u1O z100{2PB6&c_u)Bd%^q&}=f{^a#tD^zDH49-4F~Suw>MNt<(VTnFV=EO!|8AN@8c9C zKRg#tW7xRS@Px$92POa3+IkDH{rUZQ@*jtmiihH@Y=0JBXsDke_2KpJbg$SC#SMoW ol9=smcX>}^?3t35-oU`jATH(<bMzRGE6{xmp00i_>zopr0GfhcUjP6A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_h.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7c424b7a8f21e9bccd91344b03dcdc54079c13 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XW_Y?dhE&{2`t$$4J+o@V(>q+{F={aeeRonj{Ebf(i3vC# zHy3vPbnaj%Tl)0fXW5PPn|KZ<9%J0VSYQAD$b^Z81!)y!PXn0rZtve)&A3QHk?qgV z)`bn7y}gddPOvWjoc}hGiIZ92<NNdepO^(7wZ@A`eQ0-L{uh{aVEy~~nxzNNGx9Na s@&5SoLhmM<LC`GenGzZ-42*0H`*+xvsU%;l0=kjG)78&qol`;+05&RJ6aWAK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_n.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_ctrl_n.png new file mode 100644 index 0000000000000000000000000000000000000000..47842cd618018392ce7cf2ea4e026e827d6bccf9 GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_WU{ z#WBP}FgZaY<A6oxr6vELSSji2_fKS95*ZUSC&f5!Uk&FqX0wJ?R-cJE93IRO>e|}c zk+UAM85kP}r!lCjtEaLB$gmwgz>stC$KT)OZPMm>0z5HuIA6ZHx|(OceSI8*$-_tY z_t)z`FyhQP@b2zz!zcg$|F2iHx3?E`Zo0+9P|Pl$dM38|B+xMop00i_>zopr05SJO ANdN!< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_d.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a9417b4ffb41cd0ba6993cd45f2698840ca319 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xrg*wIhE&{2`t$$4J+o><XQR^rCZ%(03yweS;XXETH_u1O z100{2PB6&c_u)Bd%^q&}=f{^a#tD^zDH49-4F~Suw>MNt<(VTnFV=EO!|8AN@8c9C zKRg#tW7xRS@Px$92POa3+IkDH{rUZQ@*jtmiihH@Y=0JBXsDke_2KpJbg$SC#SMoW ol9=smcX>}^?3t35-oU`jATH(<bMzRGE6{xmp00i_>zopr0GfhcUjP6A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_h.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7c424b7a8f21e9bccd91344b03dcdc54079c13 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XW_Y?dhE&{2`t$$4J+o@V(>q+{F={aeeRonj{Ebf(i3vC# zHy3vPbnaj%Tl)0fXW5PPn|KZ<9%J0VSYQAD$b^Z81!)y!PXn0rZtve)&A3QHk?qgV z)`bn7y}gddPOvWjoc}hGiIZ92<NNdepO^(7wZ@A`eQ0-L{uh{aVEy~~nxzNNGx9Na s@&5SoLhmM<LC`GenGzZ-42*0H`*+xvsU%;l0=kjG)78&qol`;+05&RJ6aWAK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_n.png b/Templates/BaseGame/game/tools/gui/images/add-simgroup-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..68252b834ef1c274f9bc59c8b2f67a8996628a72 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XCV9FzhE&{2`t$$4J+o><>Zd0sclhkMaOFx$hyUl-*Vju% zIPiprhx42iR`-**a`oz2b|d{Jp2La97&kE1|NC=r!o_RX(smR+c4N}JZC~?)VUff^ zwm<*=N?*>`(c!VOva*<4|Kx;VhgFQ8f|WpfC*%G7^~=vR?63bHl*X_vV@bZmo|>PN ma+oDF&-VK8h%hxUFf*_%vG;ztN%kVpbqt=aelF{r5}E+sYF&T; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/arrowbtn_d.png b/Templates/BaseGame/game/tools/gui/images/arrowbtn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..be39b42bd188686dc7a8c1c67b42a7856aa07397 GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggaMm9kW!PC#LnE-`+JY5_^EKXmY zWX;E*z~QX@@L%}4Zsj9fI%SO$KKx+vU7LLKj6Nd=!!_B1Em~`sX8aIpNYY^mc%#J< saChIXBdcwydsZ6PF?=}AeI}dPX#$_nSFY)%Kyw*9UHx3vIVCg!0Hwt~^8f$< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/arrowbtn_n.png b/Templates/BaseGame/game/tools/gui/images/arrowbtn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0fd526df64055f452d6dc87af51f894a44b074 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggaMm9k;>%RL&IY1$APZ!4!i_=FZ zZ4_cKU~!%t^Z)<!(w8qXQZvMu<t!P3Z4A%+6I5Vu@O9+A_Ev>G!0F4Yn~X~YcGfR= qc~_mmO8Uaa16n(m^-3yyaF(8v$R*|dxcw2(SO!m5KbLh*2~7Ynr9OlJ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_-x.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_-x.png new file mode 100644 index 0000000000000000000000000000000000000000..6f52027b761bbaf8292be18cef8a4f76fdb3ca52 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XLOfj@Ln>}1{rUgjo>{e_v(d>Qhc_X`gyB)$A|HmMOcKTh z8VrT@0{8#(3$c0sO;>11oOS)MsbF^?PlH5~!#?f{hRn_k0_W9_F&w)%#e#!}fy02I YYWhkBgAJb?faWuJy85}Sb4q9e0JuCiBLDyZ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_-y.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_-y.png new file mode 100644 index 0000000000000000000000000000000000000000..613dcc43f15f9dea204d7f42ce5cd6002c2db244 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xnmk<`Ln>}1{rUgjo>{e_v(d?65@Qcf51&Mng1$hr<UeB- zgNB(e9;sxoT~v{7@Hh5jUNDP=BlB1mL&)Q51v`HaV?Ac61yYV5v`#oI;&s?0D=9W1 ziQ!Dy1jWyd5sd{3i*MD*XLT@aeAz5GO}t^H55t@d694KIG)wu`o@wD_V3A;$`m1%x Ula}jpK-VyMy85}Sb4q9e01fy{mjD0& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_-z.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_-z.png new file mode 100644 index 0000000000000000000000000000000000000000..6bdc9cb9147df2d83f7a29968e0ac8f29357efa4 GIT binary patch literal 202 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xay(reLn>}1{rUgjo>{elbH0kQ^5Oa}o&`5p7qbN<Fc|#$ zl^DpesIkJ%PuPKzQ<mA4*TKlcf?@Wq{{k=n@8H?Vf3-$_x|F~%aW?4-8ISfdlr}P5 yVP*RfbD$x7(!>8%HJ=pDcwD~Y!Nb5|z_6uyw^^^`oRdI%89ZJ6T-G@yGywo&b45%5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_x.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_x.png new file mode 100644 index 0000000000000000000000000000000000000000..305caf6b5f34bb5f280a5534b89f5d86de7d3af7 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XygXeTLn>}1{rUgjo>{e_v(d>wi6<dt4*wBGi>Hno8^oKZ zIDDv8c*mZ^oKjihdgRg96>JlpF|qoHKQL!FeDlPNLK9U!CT0dE9tKm^l?-|f|E+<h OGI+ZBxvX<aXaWEoRy9%p literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_y.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_y.png new file mode 100644 index 0000000000000000000000000000000000000000..6f7988bdf449e8cc875e9d9ca7ee3bf9f400b2e8 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X8a!PbLn>}1{rUgjo>{e_v(d?65@Qcf50iq=CdPnXUWaFu zJe~m(4-WDCmlSEJuoKwNo+OZSAf!3Em@!0{r-?^U;V1*6!oIE)2j;TU9lRb5SvLKr z%jU2tWcoGiQeoWgT4C~-J7EpmgjJHW8C5cmr8O{X&T^bln31ZM!=P}0q47cMicfnd RRRG<>;OXk;vd$@?2>=8ONksqv literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/axis-icon_z.png b/Templates/BaseGame/game/tools/gui/images/axis-icon_z.png new file mode 100644 index 0000000000000000000000000000000000000000..1b3c12f80285d95c28d4e3e874a690bb7019f896 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X5<OiULn>}1{rUgjo>{elbH0kQ^5Lo`wT74mUglIDhfNM^ z8oYTq9v3^>OgX21o%Q=dy&e7MS$CLNJD48cz_>E5L;Zj|i^;wWmSapFvnIaDSYjdD lBjv!#%;BmS8p9xvz)-cT&(FYK<~`6d22WQ%mvv4FO#oC>J%|7R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/button.png b/Templates/BaseGame/game/tools/gui/images/button.png new file mode 100644 index 0000000000000000000000000000000000000000..1c7361e25e5f19d68d0029199ada0b836793d137 GIT binary patch literal 1153 zcmV-{1b+L8P)<h;3K|Lk000e1NJLTq000mG003YJ1^@s66w&ga0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$BS}O-RCwC#SUXP}K@c9FPYw^8m%%oM zfKd_!DTF2uT7E!iNEt<<u%(pgBBcS!bO_2IOp`KI{s0x?;S?bSpu}LA*gRsuHs)pF zb9UCZ&iZbTmk*hO6~ivyeLFWhdpom(B@aM;uHP(g-+hF8XFEe=JWRy*d2$j0e!q^$ z;SnOETgT4nZ(ly;!SDAEO-)VZ*VfkZv=NJhO~sLie4K}vgm3Qe?}zp_FB~5o(zju+ z6^0`Z`KrZ$3VdB%U2t}GcBwTZ5BWHc`k-7VO4KoAg~x{(D%4bU%nOerpE+7)ny8xu zW4?)S3Ce6`TOf0fK|F?Yan1t%O$O8RH@SqPqa)foxG~>&+)-D51G}Jb<|zhCP|jOI zUg3P882CUrPXV_@d4p}tpTCXQ+XeGZ<3=<}1sfal!sGmD#BGeMudhRGO$~99qB={_ z3B!?xd^I*w1*0=FGmuKBp`){t60z3`!;y!4oJXB_D!Zm<XJ>&(Au5w!fj~eXmH*?# zs7(5xrQ&%(#>rJxRl$aa2B@p6qvY=HZsFSy&tx*by}iAl2X>1e(r|NgGc+|d0Z~Lj z{9Du*ueP=pR##VtnF62N?S}gLda&E=l-S<hE>wWHLbehW5e4St1sNSPFfafuEiKds zPkMS_b!A0Id`#AlIG%@GE(c>{W7M%Upy1kVHXB&2){D2zVkzOt3`8j_vG5A69MVuT zlgg&HidbOI5-RG%$}hp3(>ORd2u`OHs;jHvX>Tw5Tw2l*wfOk>c=0qw5{bl>-Wrd` zOY=fF9EQ!!%@QAIh2c1PY3WC4UWmnFK%|%#CMG6?c|pZ*Szai+UXWS6fNg$$9{SW? z+ZgV0xoEwh=F&o5!23TwY;A3&WY%jN;<#IVeSJ4jFU-%)K|J<{g4<i0I-wsBd5%0` zy+E}|g~Mr;#D`Q8;H3)G&@LG%5QKiA1=4jbV9CWO$yjc`1?;&|niS!(hN9_pDlSlP z!4gC!n3r4&243JqxB&~yJ&oyPl7iT4h2f&pi1mUGBh50o#$l*f7F_cfjy$ej@X2bu z5X3O0Mw();Q$lc@%+(9QTTm~&dh-EB-oJK|v6qPL*^8H<?~4o1wL}ijVsslB`?`yH z!4g_ra7tFI4<oMK?f|>P5y~7K=oBy<3-B0;jY=*TAxabYA$i-pZKMXUUn;rc6~N=6 zVc*NP(N;ykt5mL7aNs<1ZYb!O2M-?=friG$P;!4?=L0Ps{{4$ORu-9j<$r<PDJV~Y zd3WWzUAVpib55hn?I{BHJ32xe(Wve;YVn=zEqWU9JIJ1#oaiEtQA9$?o}Pjl8|jA> zKS-w_o6TNI!6|uJDn&CDtvV!(uH-=^mE6xJ{_apB_F7?hI-SZQALk+VPk;dcQd{ug TkdaD=00000NkvXXu0mjfP<ay% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/camera-btn_d.png b/Templates/BaseGame/game/tools/gui/images/camera-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6029aa9155c93e4e830334b97ee2101c4081695d GIT binary patch literal 547 zcmV+;0^I$HP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz#Ysd#RCwBA{Qv(y1Eqk8feK*o7{US~ zTucKRfK)w9aPOIoj4-ydiN67m5`c0a0OcN_8^Q<*SfC;MmF09CN^@HnGD-tL;*C)G zcpz3l({lib4`R3k2poR>`O5I{)fF&@n}@sp>zA(#Y;0@{%4&)XvhvalzkdE^xO@9P z!{dif96;(76ciaa*ti*P+`57Y6QD+hP1{y5C@L$0`5*(_yqy?KEDagNrNkIy6{Hy) zTx}SvY|SC+xw#ptD_c-p!cbY(!oVZSz_4S-W`^0*=Q3E?n==TB2!LH;YGnj;$uEZe zyN)n00F%|-JNFr`UcJKb{?jYe*kNV`a+nzyzJB`(=1WM5foW?8bD*nO8F+ZP8T5^H zfT761AT9xsd;1Q}5JpBQMhnGXzkb8nzkmJ0pHBY$0pfoQ3}WICTkhVv52kl*-pBCx z!Ba5*)Uoqmv3s{6a`N(us9|#T$`uAqL5LxsK)!hH3IjLL3o2?1z_2*X0M21R;@H8H z3}1jTr>&#IaPRJYlyrhK7YYgsGRT3V80f9X51xPxz?#??85uDofGJf0nu-&Ugh8ow lAu!c8U>GteG9W;J0RV(#mcF#qxC;OP002ovPDHLkV1m%r>sSB) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/camera-btn_h.png b/Templates/BaseGame/game/tools/gui/images/camera-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..a088f2213ed9958d7f39ceba26b830ecf764ca79 GIT binary patch literal 551 zcmV+?0@(eDP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz$w@>(RCwClQ%f%cQ562BC^92a5>tuC zv?`Gxh<CjjMED6-Z0zhTBqCb=g|M-=5RoFO_bVb2q7}kaX{@MNP|-%~+^dzUTCsAH z$+`EOd%rpPzV8a*a2UT04gMhh1Vm_W>b6m~MhmMuOG0!_y-G@%k*$Y%o_GL8M>6F$ zlc5B|Lskq7_96nxqK`f$_5$_RDQ(2z*cgcAHTVM0?K!WAl4yN?dWKf3MNzR4`2~3h zyau6Oy5YU?TG)9-F(5599cSmK9E5WsiX!IbXJIfJI1mHq>ghyneKqvCGV%-aVCiUu zxv2rs^JAkEpQErzFgh|0F*5~r`vRsWr(tesL{_#AMJ6Na8fp*-1hD4V1d0Jwy@Jc- z#KYtLS9Ow-1!yz^{C+<#>h&_u&8=oWl_W{1s46EJ09l{Idp_UYcZmhzyL4=VMC*h; z3B8383IRFt$6wS7mFH!<12@-RT+;qv|B%<*L@<j&Mfu8!%jraVMhavCA$4$kbOJF$ zf{8d-TiJ;C0odKz!xJf2T2=-(aT9B{A0Lk{ONRm`aWDukO~e4=V`Y0W5fj{0O2oze pF=gh~8FH;H-m${}dd2So3;?^rml!eyza9Vp002ovPDHLkV1jN~>XZNg literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/camera-btn_n.png b/Templates/BaseGame/game/tools/gui/images/camera-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..372a267bb0d3600b82ea0b8764121e98663033ab GIT binary patch literal 497 zcmV<N0S^9&P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzlSxEDRCwClQ$1?}K@fc#gD7VaEIb?G z{OS%&qlG8rto#`X{tJua{2g}r1y&X|XHr;M2?y8sg|%48fePx*#0z>cRjl%0ncbP) zx3h0%#qoH=U-5uD;4UPhUaz;MwFAdsFc869#-62lIxiP8wg?lD9FNkz6pKZ)S}io2 zO`J&yuX1RY2W0F+n$HkVn@G&<c8mFZ&YnafVQ)4YM59p@3I${`8SHjDOs7*UmrIlO zXR}$vVljB0$0V$YqA2KgyU68oOhg2YMg!$?8R>MIiA>W(wOU26-?qOM;OD@yEDrQ~ zJ#;!97>0plGKqXXk4mM&nMghOz-%^y>$+I4*VomFL?RGE!1sN2((Qy=t;Si&C!$m; zu}@W1wyV|ZXNid<49eiv{AQmV4hJq(DwSgAWHMpf@ApYeUXMm2_L0B@P1CM5p*P0k zaZZ&|ACJdusXBocbjffy1XWJgb=Kyx*?#>zBra8Ezu$94L=cAKI5&j&YAWsA#cvmG n>ehFeSv%aZ;(z_(lK=w%%F3A!7|fkC00000NkvXXu0mjfr9Ila literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/checkbox-list.png b/Templates/BaseGame/game/tools/gui/images/checkbox-list.png new file mode 100644 index 0000000000000000000000000000000000000000..04ca2b0dc9de6a4c68da9435e30854262f063cef GIT binary patch literal 857 zcmV-f1E&0mP)<h;3K|Lk000e1NJLTq000dD001}$1^@s6CR}o20000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!`$<GWRCwC#RZUD2K@^^uE-fvzLK|92 zQ$RsNqQ)AdMnpo=aKMNd(GZ9cBx;l&MTn7$aPWl05WVOX6Y-#)IUqvP_;bOwiC&CJ zg@BrfQA=&RyR$palr7m(Erk%{ftP*z`@Z+R_vYJONC2SQ+6?yBZ#SnKTwGL$&fa;p z00~($5kO!awh7t<tq|<LRe5Rg)|A1H+#TNe@9*}o^A8^btM_EhE+58NcB$`isQ|gU z(rS#Q?szLe=|3e%#CUiqHf9>?I3-9YJ6lDvR2BsCAl;$lb&BTaAi?g1yuDRn84yJb zu*7#o;oJ~VQ7$t{lH`?SSPTdmCOB_5gi<jC<Q_77eXR6Cl9(h(Zljrhh*-*D&C~N) z3g~B#s0+$50fe-sZ?9w^9u-CDCv_kbp;O5oRa{;PV)bSiwk*CUP-qb){KAkEddXU& z&MPcrWluyIr)083UejZP638p}$)6TiR_cZ-W>ImnAu8u?oOD@*yj?q0Q;hYA5F`-7 zgoFltCR&81DK1jvE_A!y!P?r|zh-V~Xc>L<bfkG~Yz$0HOt@lMQvuGM>FD_t3YV$l zQob6u2k-9vH2=AoE;hgz!?eB;Q_nrUOn=P7;V@bibhWduE=0@1k_7S#Y$MTVG#TWb z_8V)e>qomTUaMo*e&5>*jG?T&WT@jx+XHoZ%PPOze*K|{#ez4}zMh6-r^~{Th}~|t zJ!xz{J`NEg9LFWA++9<3Q<mjLUJ#8lGjmM{p)b9C?f11ht!y%xa8_1UqRLs5=P$MR z>rWi%CNc@JX&pSY?-r-g5R1hkI2;aMr_&`evCmjA800Fe_Rm&U9T>>e>wKpg>-^b9 zBevOWg27;r)#Yg_Dk=)-TML~|Cu9@aR#LR!t~oqzpygPtR>5pG<2ZmRA`l3~2I2E~ zJg&*fNs;GyNS#R-YPA_SQ?JKr2E}|Et#U+F4%V;oxTURfwWX!<&A<Eq1!60mj>Y3* jDreH5o-UE^w*UhGS!iu*@!J2m00000NkvXXu0mjftyPFd literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/checkbox-list_fliped.png b/Templates/BaseGame/game/tools/gui/images/checkbox-list_fliped.png new file mode 100644 index 0000000000000000000000000000000000000000..2db4489decd3df6b1812c15503374c1362b3abed GIT binary patch literal 960 zcmV;x13&zUP)<h;3K|Lk000e1NJLTq000dD001}$1^@s6CR}o20000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Vo5|nRCwC#R$oX{VHCgjyLbPhNeX2? zsFj*1<-bM?pNfIvveiR@G<t{%BWc-W=4d4-)ssX;hA+MKP>SwBHq^8*lnT+SMnPcu zlqd=ApL6s6=^VWqn+Z*Q68yM_bM86kcfNDZcewYXC5nP)bBlg>J|`tn=R=tp<m$8c ze`rbC^hOjL`=Zn7_5phebO8wEMIa~8-I0-z#fTqS2lya6p8+QtIZzSmWqUkG-eHC% z%q5`jr%vjX7)u7>YTPJ2OsNb=g~51HFav)DbQ885gUWj_&DHK<BOodbP9p4}MJ33W zfwdt3bnk!*p!0xCK7aipMRsC<Jc1|`K*W3*a2=f90#||_Q}oE?2y9Gk-O^e*1whwi z0NTT#x1n7QE<_taXu3fb$DxKF2!I{(A+0wcXT;d47!dJ*!3g3vi0lUEMc^)Yp9Xz7 z+Shka1d_n8L}<!_4FC#ZDzXo@Uy}WzAT+MXIg^;?urjA)yRg>s?M^u_(R7d}%!)`y zYgIF%pkySGg%jP@loM(TYZtT&S~sv;nR52Q#i9e@{l||b;=D(rCO$gpBOA;#WEczv zZ$(ALe?e}yo9gfHFIufB5kRcp?&0nr5T0}*E8>UWY7R#ODAA3$oiyd6qoZ1*(b%Zh z>+!NNf*jvbzu}alp|-ZRjpuo-*=(+c8;cIP-|x>weFPW>ayT4Tl}h!yqod<qRaMpW z{QSIPa$+KXX=&*w{*IcOnr{39T3A>(U0zo9dVX$B39`a&x0}W|&QVxc_{3x~J(``J zJr@UplxRT^v}Y?TXI4ONX=y1RA0M|Ppt`!c4{Dh~JNiX`)Q|#U7{>2(I&*$aO&#Vq z?tXD`akJHG?eluQY*$xTF4{w(5Q5gOIm^q-Bqcfdlgs652nK^WB_$=z^)_3t-ydL- zl9Kqr!NDtN4+H}Erx&9&-Kwd1fn1}}{LISAnnplRZ_g2n#nKEx4@T^9SGVl!Y=3=y z{hijAt+zWnJD<0=x9=r{_|nqSx@|Vw<Mj0OVDt-HS66qUw6v7T%ga;wd_ET2iz%h1 zriRsOwFu|LU^f*kD@{yJhKGlTS8rk5(9lrvz`y{HwTYrgz=2{F3IYKbf#+9mEJ~G% iS1OevP1D?80R{m08INcd4Ba#U0000<MNUMnLSTYQTDsW) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/checkbox-menubar.png b/Templates/BaseGame/game/tools/gui/images/checkbox-menubar.png new file mode 100644 index 0000000000000000000000000000000000000000..a3314baff139f38a16432726f26cb4cbc7328d1b GIT binary patch literal 1027 zcmV+e1pNDnP)<h;3K|Lk000e1NJLTq000dD001}$1^@s6CR}o200006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-UJ2<8ztIo-roQK1CU8X zK~zY`?UPSzR8<tlzjNnxrZYpw(x#nGo3?|kl>`i93>Yd-noPQ=T5GI^&=^~Y8U?DA ziIIh&3s*=?Q#ZO46JepQ-vTNm26TfnO>_e!0|Z0^{%glK@4b8PyDoTd`lf%TEunGa zNp4==yz`y!oO^yl3Q$;Q(6OOMy4ISU?Cc2apFVpTN~u*(I`wSM|E{f8y?6V-6cME= z+EZ%fk#;`X-$z8}0N@Yv3cGr(pa4{N;zCRT<h=#}h^nG(9qBQ+Z4sQ~ATaM+S=8OK zyPh#Rrxd2I+`jlZ7h)115R>Cl`|UJ*mSvHYGAA8MLaC`_)2<yvlq9H6S>(=FxVOvl z@N7Zk9N=91#f7XFLQZflZm`ImV*n%o+kGyGx|3U2F!uZR4)}AN%fC67aFl)6t!}!< zS?ojrY9bHKUshy~aw#rE>WdcvZ~ztn>0NhuPu<Ba3_u7#7(k8t%>qyYVEYASr>oZr zj@!5CRu}G*FBaT$x=+d*)<HSjKYjMDH&fN*w$0mexBmS3ad(u3J*^bYo3>5~;Z#sw z71+(8?mi*pgl{byKzYb%ALGhdmhy#}%$9m)Cvs#Z#$1<4&IN?D#!b3;VpYnOXxc#} zH-K^g8$-FjJ#QxEUZ;#TmVN**>?T_7?`pZ_dX$sTnY-r!QU1Y8sV2MWvF*NFWB|Yz zDI%&U7zx+1d5WFaN}ZXGOeQloFfg!U<$c4Wr%r!!azxj4%*@QB7ywigy!Ps$BmWeQ z9)DidzsASMKY8MpTenAqkPt%9QudaK^WT1#<y?Ss0mCrZs-kxf9~~?fjUFK-T3X{L zP19T|%3YeR>+c&nHU8#%g8(p_|LG0R1$wr2ojCN)fiL{+l_`Jw;QL>4!CQa4ls_{3 z{3|_1sgy`0;$QC_dEpE(rZi1kN_nO~{h{MH^Onu)u3x{ok1_UZ_UOUSf}x-ji^W7V z8m*+<FbwwkTcfi>FFrG&990xTV)s)!KhgpL)!5i*x3#rdp-`yONhyW7xjAi5de@CT z=_e;^!=e1kdk1GX)zyi3JZ?uK5y#(NptrZTRZ2<i?d@cmCVRN6^KPd9=`)dNR5mv^ z+YJp3!V~DajzXaT%4V|x04X5^vn(rUSym7V6b#nbwc)U(X_~WW8~F!KIa5Tqf6BdB xGv)qR6Xh$#|Dyj!r0e>clD)h#<?ArY{|m`Y3QkQlILQD2002ovPDHLkV1mS;-T?pr literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/checkbox.png b/Templates/BaseGame/game/tools/gui/images/checkbox.png new file mode 100644 index 0000000000000000000000000000000000000000..46e0ac959fc26d224015f316df050c2f137af4d0 GIT binary patch literal 3943 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 S3QYOT0XMIk>c74QkN6*F=2BAt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-btn_d.png b/Templates/BaseGame/game/tools/gui/images/clear-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..229c71e8bddaa102f495a3dba47004f9c65cd0d4 GIT binary patch literal 593 zcmV-X0<QguP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz^GQTORCwBAOi4*$V_;wiV;~-^VgYIJ z^73K`3=AZwdj0zK4BNJC3j=9_Yj}S9HrJH2v=|_5{O9*?E*4hSZ<4C2XLDxFS^}iM zfz$ytF)+bQ0cmLV^{x8#{X17&XJ^}@moH;M^!w+}<v`*fd6-(bCXfx>0s?QkPn?)w z6CVEP+t+W**N+`j{PO<2JW!f(R$hMapFe*XQ8a;^<ei(l{`c?SjNiU}W1d%1Y=3;s znq<>}fcc&U1q&WrzGU<D>sRJ~|Nb$;HG%v9vf=yp@5~EJ%j{0ATbF1a6f`foz5TGU zkMHBZK=}_JKCu4%`<IyorU~Tf151`D+&Fns@$`oEiIyQDb5kZvIQZ+=FQ%ibRw*#E zvVI4#|Ni-d&;$ylbsZfkKtITVYyfHa{p&Z==TDzl_RpQ`C$6M?o|}{F?|-2G;LZaD zD~RS66nv~~V)6)N>b@n56}$ZWa=w55ESxrV$`TGP&c7%D0g8vBl`A{>#l#-3tgkQ2 zR8m^7wz;{KM@Z;(+3GdzvKks+{{Hz34m}oFOoJj$N=5Z+-m>NMzkdJ5{O|8yMqVD? zKm39MzuDN>{(%B$^X5%VKwbYCk<;G)|Nj|*als698ry$h&@!{I{AFfl{tFTZ1~u4K f&!0Y90T5sS_@w(H5qG>u00000NkvXXu0mjf2Ztbp literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-btn_h.png b/Templates/BaseGame/game/tools/gui/images/clear-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5e67cb13b2b4a85172bfa47e74b66993f0b315a2 GIT binary patch literal 595 zcmV-Z0<8UsP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz^+`lQRCwBAOi4*$W?*12U?3h`U;%0H z^73K`3=AZwdj0zK4BNJCGhhO7U>Z)GIKgXfZeFjXq_kgNUj7V-2Ju1S$m*D2rhp`p zl9Fcr{{5R5=;ZW=4<DL<=+B=&tAoU08bN>s#>mglFXG_fcy{jGxe}m8#*ZI9GM_(p zPV4K}uWDReT(?V0OVju5+t&+J`X6r7>({RxLPA1j0TnU5ee;GTDJjwa)albT#>U3& zL4koCPoF;ZdGX=}>z_Y=nBbcJ{{71fH0}21PoJ2xva|ipoIP7*Vq(%dVZwyX5fPEM zf$}_W-n;>6Vum}9nVI=>M@NUoh4be%&z?P7X=ZBLvS8uDP2azNXPP-<rYcbW%eSvz znScNK#RAtPDk^$l*|KGI-@biQ1=#@7@aOj*#y78Cu>ylRQAAkeFdrZPpa1{=Gs2xW zapJ^25Y5TSd0R(E=N8C@o}O;)$f)S{??1kax3#zR@eA<(K?!mT3yaSa`unr^czN$E zUc7iJFaS<0UAAlz7dO|l>C>iXT3T9u1p0>&9^{~y21T5Oh2^KYbLX{x`t-3I=nMva zet|!dQj))bw*LbK(3UM*m>C%v|1%<|J&=2U{P@8N#BBfm{$pZgW%<Lx$_n<!Utp+# h)I56d@C-nJ0RZKU_!T%UNAv&y002ovPDHLkV1nFEBVzyn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-btn_n.png b/Templates/BaseGame/game/tools/gui/images/clear-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ecb13a8d661317f38d3e4214fff3f0312b7b8af0 GIT binary patch literal 377 zcmV-<0fzpGP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz8%ab#RCwB)l08lXK@5e*yAx893m}0+ z4?=K&2#FewK!JcV4Fw%1pruKQbm>wAA<=LJelEZTKubwTGJIwqVjwiI($lj%e)b#h zSV}4U^((?ZzW5oeF0X{QG;0x_gI(Zp1%{y0u%eoqB*Be}+Z35ISogfn=j{1}M~e5r zom^~6<PFZmU-o3Ftwxc~`|t%grt*PxT@s(Tsa(|3ghnPfB-dAyMYsu4E~+@#kR=x! zAn3v6q|U#1>`il#Z>bB`G@&P}3s^gaM{dn?`64>vd+u<n30q7b5HDGMKKZsi{|^>y zC)Ag|rg#ir`9=|JM=PFw+g^xf&*YE1hYH5v6--s@cbdf?FJvm;W#*#H1Rw9<U4Q`q Xq@h=ZBb5SJ00000NkvXXu0mjfavq$! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-icon_d.png b/Templates/BaseGame/game/tools/gui/images/clear-icon_d.png new file mode 100644 index 0000000000000000000000000000000000000000..ffca76c891b250e7c496951e638266309167c8f1 GIT binary patch literal 621 zcmV-z0+RiSP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!4@pEpRCwB?R6T1{K@>gb?XDY8V_X+S z3q>rfB>n)gw^_7OAxezF#*fZI0@~OZFojK!h>EO`kOUHqLCX{(h>fLSXC;c@M-_K- zJ=b?%oPEa1fxGkOzBgy?Irq%c%<#XX>OUmQc}8S07sF3ni`jZWN+X*iPw2&2A+(BQ z7kK&ucznieAz%Ih%hJ>DlZ^%PK@GV59eDm47;Kp}UIB-{0x$Z3@p*Fo3b`{1$SX}W z$po;tNWL8+_i4Rdu}z`9*y_)Z$gwA+snTbW-{kv>O6U|sesES4+GgLP<e!RFpWEc= z0pR9m;D$;#G)W#;zSaZc8ffROjR0oJAsss@fXgFhQy+neCU7b!RSFPCYrO+rhFF~d zSyP3>bztfXa7h5u4*VWiQSMl1QYkd4IoHs^x%T->0i5^zL-L~5&3M1XHA=qUoOTD- z$h}4XMd6%OyXO@Qo6Q78r?+~C+|%QU)CUKFBc5v~6v_bpybCxgzDs4ZnHx#9jjN3V z{dpH$#J>P0<Rx((`=nTRZfN2a)n%k5=PJ`(*Lt^NM`+-Fs0g6xKJxDp{pwkAC%P-Z z1s%8C07mbUC$HujQ5x)@Ax91XZ$#t6oM<Lp?9wZV3z!AHJ~xv4AlmqQxCYW_dem$= z0dR_)6ol)2&z)FJg)iOWP7+rA$2BiLtZeXC&@JEMUjhsO#fZznno%yB00000NkvXX Hu0mjfMO7GZ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-icon_h.png b/Templates/BaseGame/game/tools/gui/images/clear-icon_h.png new file mode 100644 index 0000000000000000000000000000000000000000..e424b7e0eab203664416618e7efbd23ed490704c GIT binary patch literal 670 zcmV;P0%84$P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!KuJVFRCwBAWWWTBP|O6SVc<Uy|AW&1 z(KR3grbZo|KxRh9IG}_dOc1DZ^Y_1hm$qHIwgJffi*6Xq5XLeM4M9Fu)_$OX{nx*L zJ0E}hc69NB2aluW<>h3#xNLY?SjztX|9|<yhYxA%o;-PjY#0m75TF6`fd(iXc>UUb z_lp;wU<#H#e0UE?Kd98y6yRZDY0(!Dm<==}5-9cq<{GfSL0$kFpbs>_4{89&mft}9 z3q)6IX+?qz`1t#GJ<zMVh3e`7Kyg-BxPb{!7=XNh&430Sofs}==IKBKa=UKanEn3O zuReZO)(D^+$QR5oLzqB;2nqwJ^Zq~$`3H1i0xKiq?9YGx<Nys=0W$dM_wNURVxCY} zfDB=RM<XZ@q2T}y3!njsER2kEKn8SNzrGUc!k;T1J-UOQ9GGA{P%OZrtO2_LAn*Ok zRaKQjPgqQ_bO4G*5FZ$ba4(=60AdTVvDpEmdJD24U_V)ki2MPDeuKHNFf%s`OCKl@ zJ8#^;Fd$1sMM#j1ZONxUe<mG!^X3**{x2+fvo`7ItOdrBHz*oGu>jTh6UhKUX->|) zKvz7jyK*HK7Dk{*V1_vfXv01+4h~IDW@b-ALBY@R+}t0oeEj$x7zT2ll9B-+7XS^o zbMW=+ocmwD{(#0HC?@|gB83eo-Z?-C36u_?(Fg{hFaU<pitZaXwjm{hU+9J~BE>r^ z)IcV*NPy+yKS&sy>=|(s0nD(}h-S!Ne7TsS@&q8j0C^uA2nuuC_5c6?07*qoM6N<$ Ef_dQ>;s5{u literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/clear-icon_n.png b/Templates/BaseGame/game/tools/gui/images/clear-icon_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a826892744aa24c15b17cb77f92d6661a9250103 GIT binary patch literal 651 zcmV;60(AX}P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!ElET{RCwB?ls}9TK@`TFS@}<h2cfYz zq=3Z27<%zavXLAI1$c=XZLH4LRtp=OGqJJ2#O5wJ3JIXG(?kdbEd~<I#gJ@A2pSD= zyTao4))}(d^K5RCVVHlveee6;F1v8LG10jgWm}<}Vq7^d*c&evi+RrZJ;vDf`u1w| z&M=I_YPDL!bm@di2+QSi8$O@!1uk#ty8e7T9=Ds#rj|~pHzSeA4eUKOP4mleIDFXY zbjEhrMG3*7HvnAe^?F;~Zuh4=&}y~5VI1c3`Ac4}_gO3!D+98NwG$bQl{cjTz;yt& z1%RIN8{MF>P$=vYU^bgQL8{lX+3XJ1T~fEdKhS_u_!j^Vxa0A7UIFl^Uayy@)9Jq7 z@4t;b%0iJ4P9l;9BD{rwm;l^Y6s0_$&-Va0BJgA~`HVGHL_rX~wvj{>I#^l&@BoKj z6QEYBRYZh(yWKu^mIIeFazR>l27uBtR8_s~EG#bTfNZ2WL|muf1knFrFnAMIzqdoI zoRZ1p0_s0aBoYe7eIep0Kqiw32Lgcu?7tig24BVgQkJ)?R4ToNOSi~IazR|wZGeq% zIQ$V&Xq8IkuGEN3P-I95^HV4ky6Sej)p$JqD;kad&@}BkG}ug~Qacm@0LT4)e{VDz zod^fXNt4;MA>X}JNK^;HMyrtq(CDbqXne4hf#HNOo9`|G<PH<$TfDG!tL)hsp8!Qx lqXV-1-&=gH@5vtl1^}VYAYkEw*jWGo002ovPDHLkV1oISB<lbG literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_d.png b/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_d.png new file mode 100644 index 0000000000000000000000000000000000000000..984a63853c4fd48dd65049cdda3025de63842420 GIT binary patch literal 280 zcmV+z0q6dSP)<h;3K|Lk000e1NJLTq000L7001Ef1^@s66{oTI0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyx=BPqRCwBAOi4}s&&9#cz`(%B00ZBC z{P+N*3RyrLJzYHpc6N5S<ooyU1uk8^Iv*@4At}iqARqvjl>hgS0Vv4;mgMH<W)Kn< zW<Uo_40ym0m%MrXI&R5Tt5#x{Ja^_a!=uMfuuE>;x)r;fhYuV;(S;&;;NSt=u63|? zz%6O(;J_d!D}&pg-rnB0C5=o>QFLPjLReThZb@}b%@L7I2uePB_z*;W0B5A7%a^0e egYzdqfB^t3O)<b{;yon*0000<MNUMnLSTXl*lv{o literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_h.png b/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3de8387148c02f13b1e7f24f984de37feccf69 GIT binary patch literal 468 zcmV;_0W1EAP)<h;3K|Lk000e1NJLTq000L7001Ef1^@s66{oTI0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzc1c7*RCwBaQ@c_^F%aF|<O3+NK>Y)0 z9XimShOxs=YQDsp@gJb1gn_Zvu?5E&#SyLef*bddTyF3YCK)zo&)JhrF5V0V0e+4h z&!GoIxa##X<m+==n=O{N9yuURK*SQP*N=dsnwM393W#xGCjTgBh9DS%*CeD2WLQ@( zd-NMqWJQ1um_eB+OsRPw%T$R~*8MAczzo4;$qbQ6^L$>dV>0jSi8!D*$CI(frpPId zoX)3*M|j)3>wnqT_NAKB=~Qb<toza?as>Cw1-x!H26BvM$lT86^U`fnQd^FLPP<cr zPGyCunGV{Q7jV*OM5cpy&&=c+;;`H88q-x|B~$90w_0#|+|U@x-nxDJ`u*z|$CpsO zva@DWd#f)2<GCaujUp6k$YZLR-KSllH$&ElJ++92;<+-WHWd<+{$Q41^=VRkL+aM$ zAciOl_BmkXD2RnU>Qqy@lE%w=B^f)Fy&H|PPw->A71`mp00RIT5=iSlljg+$0000< KMNUMnLSTYWrq$s9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_n.png b/Templates/BaseGame/game/tools/gui/images/collapse-toolbar_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b36de3ae049d9743a09dc6fc26af740cf9841360 GIT binary patch literal 439 zcmV;o0Z9IdP)<h;3K|Lk000e1NJLTq000L7001Ef1^@s66{oTI0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzSxH1eRCwBSQcX+4KoFgjo}`B)UhJuO zuu!2Q;vefT@mE<8g<ONiLm&_^J(Pmb9Cp^3x7jpb4h+f6%X{-SJHf~Mo279aC3kac zEj>Ox_mJPyDg4SlUjSNW3W5uGwOEk!Lx4uX4pZl7UqAx0i=rnvSAdP_$$A20Su(H_ zvh10InGB}9^1v-*3Cso@F>PaL>L}8isTm_PeHKN5r}hODW+z|w`}Q5O#yHtL&*`VC zP~)7=kRBzVBuOw+YZy|xe)*kcR2>cgwbuTO8@Fkiwr+iIX4$!2->q9vYwhDG`qk=o zMb~kBVd5z4oUzQ=+;7}ZXk^<zq_MnNQXIwP3Ahy_)_rUrwy3$i&xWZHMWJJ2P1r5i zFf9Aao&X^$0y1N~*V>2MX<21r!6szg-dfzih^e4%lbv7^R`VYwdnU3IbzN)k2xeK9 hJ@8zg^|AXCU;u#ob3iiM+rIz+002ovPDHLkV1m|A!NdRn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/copy-btn_d.png b/Templates/BaseGame/game/tools/gui/images/copy-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4212ecae0a5027e73c199b5f1fc54fd951a6d1f0 GIT binary patch literal 542 zcmV+(0^$9MP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzz)3_wRCwB?Q$0&UQ5b&i2c-8(&{ERu zAmtnlS#gJcfJ4*|kcJ!t8=Bkf1FN|u6q_1cB9jIoRA^DKrXX+)g0Q1x>gl|<Zk%iO z0X^{Ed(VA8&hx(KISS_-|A9^qMNx2de0<3sAFW|QOYyk!V*yGYWz1sl!LERd{r%q= z(9V?X)igZc+}I9k%{M)5zlq^6kWZIrx*TNPZWkXjq6++51`cR<N_c8Ekw_+SlukqU zdU3Y1)7MlL&b%Zjt^(%N6w2i?VzC$)V_1nq5F8uBq~DMA?QM8Gp3m4qD)p5?vtGyM z#)h3txN9_QL#>8;!77<Z*#Es^BL}KnfQELO%OM;N+v^3fwk!*VVW3bd;iOW58497- z>p8P=Ghny3(8S=jqETeV$1x%LUKHOC)hZ4%8C(~OSe>1P76=R<A~ERq`!HT#u@(#> ze{cYw&xg_Zd8Fp%kj-Xo2JLqHD+5O`j-&PR0%K$ZrfH%jn9={`nHexKCz-+LnGf6v zIxF>jcbAKPxnRYGCb)x6r^EAmd)y_YL6MQEVg_WitE@=*6!gE`IO<z&PZpFusBIY} g7Dq7O;Z1-60NVT8+J<*6;Q#;t07*qoM6N<$g6>T6K>z>% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/copy-btn_h.png b/Templates/BaseGame/game/tools/gui/images/copy-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..3de0ae1101f1b8e7cf4b3965ebe2b63b3683a94f GIT binary patch literal 536 zcmV+z0_XjSP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzx=BPqRCwByQawupK@ffS0m*$J*r-@r zglHWsOq4790T!Y^KrBolB!#tI{2*#=BMGsxuo0tJ2$BSB6x2!(Yz0B$CK!`jcUCT3 z&WqxM;r4d+?Yx<N3IwhW4lda1tAh~CuC6Nh1v-%j7<1V7;hyB#&JO;9PL+vlXd2${ z@8u@v9QOFQ{3=Sg0LsY=5U5NEigvs09ikg}Uj^QRsuSU@UPmUE!+xOv-7s*nx#?=E zD$fMeC_NWg@i?m0D$?mR7-LwNn?r1H5Thd_SlQS>G#c$P&-3}83hH-vSj%Q5neh1h zECaW<coM2|nT*^wL?J&^Z$U#lIzC1+nUw$2;@@!`n5K#IN(G0t8mx&4bUK~x0DBa$ zhX-h4aO+D;C=Lx_SoA$3)-N|V*ee!seR+w+$w_EqW8p(21+MGD{QSgnEQa##E^OPz zKq`g&)D%jkl2p)aHh(Jc4C5slA0IFy5m=UmhEPWP^N9qQm{U+e_uT`}qn>VUanUaq zs<`k3_t0v!czJu9dpteNWE85H0R>g{J2LnR`d?nBzWwb54W$RY?Kg|xF#VteiT}ly a00RIL@7vTe5mEU70000<MNUMnLSTZ6?&#D2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/copy-btn_i.png b/Templates/BaseGame/game/tools/gui/images/copy-btn_i.png new file mode 100644 index 0000000000000000000000000000000000000000..528d6bf93f160ac5acca784fe18213794ef52720 GIT binary patch literal 490 zcmV<G0Tup<P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzj7da6RCwBqQ@g6dKoC87_(H6r_(Z$g zMGzYsK~h`#1r`>TL9n&dTKoaE^cyTKLi`5__y9{m0wz9o?p-mtz6O@vWcJLQnK>)O z<8e%<)1SlP5W^E|G#X31F`0oyQH=dv_Bdy=89tfJGY5k~Y&IMEdAVGW$z*6xBoa@7 z`~ChmfIl+>vE?`p?H?KN1Ix|;H%1r?1{e;9#Ok(}B}t+?f*?F%m!1IWbQ-2<qTB7_ zcs!!nY*HD?WD>frBNPff?ae*Fdc8)k*CQ}9%d%+kdOMvCol~ilrv#B7Shn$aj7p_K z-=$Is)oK-yNCb<;0`vKt?zpb&>CK(NcDp5KyWK`EmqRofMZI3fYPG^-GJ#<j&@>H- zqP%91&1O+36wqq5sAd*KRaNx+eH4qu`)L0$;7#EU+qMx7hbh2rx5N2-My*z(N%<Mb zT&jM-j7Fn!AP_)4pQiwPl&90lGfw%HKyLD;#r45K{wc_xl*a0QEYB|}*{|(Kv3&EG ggm3iz&aMIs0NcK;Ma?*rhyVZp07*qoM6N<$f_7Tj;Q#;t literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/copy-btn_n.png b/Templates/BaseGame/game/tools/gui/images/copy-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e88b654fcf16b26dcabadd7333fb510ac72fc1 GIT binary patch literal 496 zcmV<M0T2F(P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzl1W5CRCwBqQ@g6dKoC6};|og_pXe7T z*rgF9wWVK>!opGnTT89QA5cqw;aXaVA0YuBU@3^8Np5EDT`{>)@4&LVnVmD|%uGZo zmBM^J|2m(~Is&ZKY6-kCm4X#T(SIL%oQuT*pG=jRBasO9`#pbj90&P)p7(S*9T<2z zoqhoPnJI|Y{eI`&HIm@GWFQZeodR4&7>!04kH^f)-R)2)#BYS~ltXv`a=9Efn+^K? zJ}#FF48vd<nM?-VZWqyLG_W=gfZc9~!C=6_WTt8Il6rf+9-p(>tVe<(50q^>okG(z zzSn9s)a!M`<8iE3D=e2weq&jdtc{n!;c#GQr_({PSVSU`K(pDzcDu!FHp6<ohOX;K zCX=rj6bc2D%Vo6NZPrX6l}ZJ};Skko_0ift3}jKH<9fX!7K<^!@py!7+h{Zz+$lc; zRgUV@z|dVxCKD?h4x>~mF@S94?RN9DQw^96qT#sX4O((OWRSlL>Ze&J-Ouv;10|jv mz8}eX2P~z(1o*Fg3oro0L9ulEt1^)Q0000<MNUMnLSTZL{Mf_* literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/crosshair.png b/Templates/BaseGame/game/tools/gui/images/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..06bcf5c6ca7dcd8481c5660c7fc875dd303464e9 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg<l3zk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xj67W&Ln>}1B{;D1osdy8@szBYwCIG4*or`-fKIy=Ke83- pnwCvmC@_EH`j4KABv=I!7}iW+?Rl-@XAjiP;OXk;vd$@?2>{A;EA9XQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/crosshair_blue.png b/Templates/BaseGame/game/tools/gui/images/crosshair_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..d5b0485d7ad5ebd7ba72f05901210a275312e90c GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg<l3zk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XG(24#Ln>}1rL4Grq=Drhv!Qa&;Uf*~0#p9*AO0_0BRSz` f&z6nC3I`Z69e7L?*M3t3>S6G7^>bP0l+XkKiajbo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/delete_d.png b/Templates/BaseGame/game/tools/gui/images/delete_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6ffdf6d2ef521a04ec32341f1d87ad9f5397fc20 GIT binary patch literal 622 zcmV-!0+IcRP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!5J^NqRCwB?Q#)@HK@k3K&qSdC_t9BK zA`&68g%YR0l7$Nh$adj?fR>2X1!Y77{DjbuCPcD?5d{!IV@N^@o<TA+AvDCqGKP}# zJ$yOGxykNgM~*q<f|0)4xtX1BcOI({5&laAK?gz@V?mA^9IG7EgVnY#*opNc_?$>2 z=-%8M<#IVH7K>z>7Ok&;p+eyyWilB-^jnbpMZk^OP$)z{YCq}4%O%>__)4eaaZ07q zG&_5j*492sU!2>-O=L_@MyJMOF&KscP168H!TkJvT)uJ@M~()O&*!CYY;0WYm1zWe z2o_(htlTPH{DkAf!~4b)BO`y(QyFTtUszsV62H3mDBWF9_=%!eLZ3W;0cX!mA`*#6 zU%6byhxhOB^vPpbmIcrAlz$bG9uyu?DxId2qoeyB(6kU`vr{xPbCWuUh?sz1YeoLk z)7LRE5ksZ2g=jR2QmKTYAx*B`!u$vrQ=qB}0;-CELjyQGILP$?w=poDW?zC`&qJ%_ zp}DgI*L7jpw$xj#UFqvf&}=p(8jS`_%SOF!aNU99IFhI@f$KKmY&$SaOIBRh>#%L1 z+uXN(V6R^h_07$1L8dS{G^S-juh->GEKX9{svOwtHwV|ax^O0$M0V<uw1vaR@b=AX zRI61yf40bPW8dL(aNBnd+OrVmSP&1Z>m`0~+mhWcc~5`=00^`==%PDW^8f$<07*qo IM6N<$f)>9RzW@LL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/delete_h.png b/Templates/BaseGame/game/tools/gui/images/delete_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7a1ac2e3156782b4ac1f7579f461cdaa789d20d0 GIT binary patch literal 638 zcmV-^0)hRBP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!AW1|)RCwB)lh11tQ4q(!`!<#eLED>< z4OB=WIV4DvLSPG4(58nR{MCx~P)htC`~$p5@1CTh(bS8-PzZ{xJ=h<)L?krSgAE%( z(PMtR%{FPWao)2n%|;10FwE@Cn-8-y?<t*52SOA@0b|Tf?lh@RdI8<?jdX+bgKgXV zxXuB1r0crx&fFaG+uMl8<Iwd96pKaVaycw7r;tvkIUT><Znt?CKvmTVP1EjgZe@_q z=i${fB<?0KJT#10EQV+_3J=9*XKo^s$vh;$OHs#8wCD>*BB)lY@Or(XB`hsH#?0(3 zoH;v$WHKppV`E`))_qjuIssV!yt+D{z5W%x(b40eKM;_XsSJC2Ke4j%Nqq97ymJO? z!k>yw<MNd$1cO1DD-;UIY;EGr>sR2MgNAtNUqf7b_WT7>sU!@;!1tXUne%uwgu`L@ z{Q)d4KIj6t9IKN5zp#+N_;`e-Zy*#3A)Cz-#4D{+SWkc{v=Id^RmH%m0h}HjB;Q3b z21Z@>1+;A&t(J}E!2ueL2DoWTztw8XTwg%5*_2wZ*HPmpDwQhv7A(t>puT`cqX}!@ zLbb-_#LMLhOjGzK<@S33&LQ&M-5+kIFnMWeyoPe6B0J#(DH_EdfOC!FA>F$;If<*+ zZphfk$a!q6e?h5K!uxk`sT=btt?|iU@4=rfuybVQ#nnDkA-@h^<3}nM7CnIfmEQsk Y0Q*1*SYt)PLI3~&07*qoM6N<$g57H*C;$Ke literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/delete_n.png b/Templates/BaseGame/game/tools/gui/images/delete_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbe067cf242eafc3f9d51850fb75e2f9dd196e0 GIT binary patch literal 633 zcmV-<0*3vGP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!8%ab#RCwB)Q_pJ?Q4s$2Z7l0W(_0}M zh+rW-BuJY=U<GrqjY1CfN2}OVi3chE5e3C_?~)#(){{R_2!gE?)OIfs2@UjMLzYmy z?k2n0%{II7y=POJjSz6)V`gXGd~e?DjL_@#z(f!P5JDtsUZIFn#4spziUW$DzVADy zc>#h)ilR(BSYAeTe;>JA4vKOWnx>&rsbF*S6-uR&Lyxa}p63h@h@u#mW%>T@o{DO< zil{6j|1gj7@Hn#BEYj&TBGk9Eco(Xwexd{_%Oht>oR~?a&}=pljYe5XC=@oZxO5K} zE{0Jo7I|!XdWM~q`bnWP1P5QXw=?D2KX7?+^3)lhn&O&>1V=}|v9<M?eLDSkum@|* zp3mOE^|^T@5(yrw)oM`pcJc1*8<?gE+QhYf9tm_BFJ8W;L;^Mn&v7$5$MNjx6BxRV z)z!zr;{#9N!rs5KlE>_9igv$_WHO0zxr|68%5_kNDqy#ZqJWSnV(i=)&Px)tL)1q= z=rczWc)pKr*N1z2j83Nm)3UhT?Rq>mlE8Icj&{3^mTAE-n$)&o+cqZ}Nzm!IupJxC zmdQJ=>jo@~*%pmCBb(?P9{!SuK={zK%ocRR;FXw^M5}8<1i?9Ai?nbplflBRJKPtG zUBdToU(skZ@bSZYT8*_s=V0fb%k=jzkT6vlHrgj(qMvLd+eD@@MDV}zM}PqUDpLMO TsGfkM00000NkvXXu0mjf=UXDz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropDown-tab.png b/Templates/BaseGame/game/tools/gui/images/dropDown-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..d79c6c70e9c342c972196c1d57f1fcaeff8ebfc2 GIT binary patch literal 1062 zcmV+>1ljwEP)<h;3K|Lk000e1NJLTq0018V002q|1^@s6L+y`!0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#$Vo&&RCwC#S<O!xQ4k*pHX0-<8b5pR zvxz6GA>~xcK|~UJphxvc6ZGUk^pB`#G^y8mEQgvH#H7Yc3o+`=^q?23U}Duo5n5&G zn}OY|&$Xb-N{vjIyqUL;`OVC}_jdNJE)Bqb#UY)?<EdtCfUzArKF-g>dyhvN!0Re$ zm9d-IJQ2aaIvoA>w?jj8wZd2liTJrRn%C9Pm&HX0g;rJ>I0dT5h(%znDT`wlbv+c3 zO5jBygIWWLL;`;9?~`0>HiMb*@$oVIVvO{>r3ES~DzpJd!;we?!r=(X@7!+q90-t8 zR_iP18Xndza-@+g06Oq#VS!`>9{C4wLGL*NAz;411&dtnKxap%<ivx|_I!a$o6OS$ z*FEh@#%8nG9sT`uwXwMwVh0D(XkJ%Cp_LU_TU)zc$v7Mi=7uiA<dW`oT?G@4tdcQg zZ0oMyiyV4EAE~_UTzX-_fr~VUUf{@!n)dsHpP!$@&hBnzWi&KE1LMQPLx?d(>S%9= z%F0S@z)dC-dl3l1^71l)jCs8<J2yv84Gavx%gIS?(>~?%(X0qE2R_WqP@j_l`&fVO zwCg#r9J-<d(^Ir8g25m-olcmV@;&T?n`RF5^z^{sptCr$-Q^m5OiQh}n@lmc*CM68 zRLR);vz1H}t6ZY8%7xd}(27zeBLZtpS)9_DObPsv9ta^NlSzn1qa@eV)IfE0HJqNF z!pX@=X1O*sL3w$(HsEM@YikQOH#bRkxm>Wcv_wwz_Vz+sTbs7WB5t>vT67=~2#}1x zV!!719DxupAK-!?2#F4~wzf)6Jos$S2e`D!Jk2v1!>r$rGrj{~yRc(ye0-htS;PUn zu9D6e?=YKJ6uIB;f6>?1XR=zYVzsyq>9j>wJzm#UP&gchjg5^n2L3}_=LDQgTri9@ z;IVw-A};-b*YACQSp|$Z4BT{M!XdDhq-5V9y9$c3l3wUXs@yyerBzN0N@?L!l-4}* zdxFQ~ap_A^Q)45w(O8UHqtQrToHPN)FG%0MhhcATkM!>DZfdu;x2eU?TyJczp{lB? z@Zearbai#o`aL>2f`jav4a#+Obzrer=#c2P|4U0PB?!cFSxM*FY_5gT(NU<cuP0Ds zl(7$=>uJumBDjzeh~=`9&hxXfGCF(ndc9OABBP9bc#XHEnD&&A0wq~VS0YUB#KeTO zn_XW1$$z%XJ>$Jp@|vgLsU+z2nM4YLaJQwVHuAfSy8*7#>EY_?Dr1C;G?fz6Zzu*s gsgn6WBL6GE0MNPiCgF2GA^-pY07*qoM6N<$g2bE!X8-^I literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropDown.png b/Templates/BaseGame/game/tools/gui/images/dropDown.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7414acca76d7a30b393481a9600cd84d3195cc GIT binary patch literal 1309 zcmV+&1>*XNP)<h;3K|Lk000e1NJLTq0018V002%11^@s6*nG`v0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$zez+vRCwC#S<g=!M-+Z*o67M|;((3o zKxu6Zq-qYx5~miQk=n`!4wY!j!6<|ie?qw;uGoj<h=>ppmN?KzNCm_JkRo4DK%mf8 zqQQ<tor47?4K|(k%r5I;2*&o>P30$^=Dp|L`DTWhec!&7&H>n8ZNw0bMDDUS#x#h5 z$D^abV#)=)U#6@u4Y5Aok{5|YmNzyw&J|%RQ6Riuh9C~&QJ+i=IOE>lUO3I1APU@J zfy~LVGF09#RU}CQ#EHk_K{=+A4Vt5dBI@6u`W1w>$gk9fQ~Fn9@uSviw~LmAU!rr( zXI>)s@$oVIol24IZfT)5kxWu+Hk;v|!=Vj$CX<2njZHW>I3T~fyPMjbogHc&47l%` zufcA&S03DGG(ty*4>mV9;ppfH4i670j&@U169fVQuvjeD-me_BY%WKtwnaX#4#zzh z92^9v(@CJHXk)Csy`46zx-TJkE}J7&+ajOWPZ}D)<#NHu$OyRIZc?-{hQINB370*a z%Yw)j`69vP9UB`{o@Vdg|5N;KSC5PzrD)W=yvv?~PM2TETrPLJqgL+BZ*$!ha7og^ z+1XjX6K<y2Y_9wVioqZRUtvduGMg-IZEXb?@qVd-NAe=42W9XOOLH+f)jeh!jYcoY z1m{<ROfV%fA=Q#bN#Ss~QB9+sJbeno;jnT6?^j~xtj|1+V&Kd3^Yc}qQHX<h1vDxY z3RQ(hA<p9BVx30SY1Btbqkdanfvv4A^85Pws9jxMrPkx|!1v#Ms~wGc`0y*3ot-5f zx4pehakN`oTcN+dUwaxgK0Z#A2|-cO#+cXZ)t*M-@=i`p5*RAl7^@nMYHVzTsi`R? zch`(Yy*oXv)2N$iR4f*w%%(MsVmMc66iah4In_O3Iy^jlNhUbIPNV)mG%A@)uw9)6 z4-340{g*OS-Y-@3di@m|Wl%E)y-uIM@S;_2<owcj)CbPFPOk%zE%GZ)Ce*XjWajYS zow*J~w#YA}Q74dko2OANE*G`QH*fMZ%4BBw{%$ePb_qnb$XC;-U!OmRwY4?!2L=YH zotvAZ*5~uV&p-WmGmR<*++;FQF?V8O0(N(IVSRm_;%IkvcEZrm5ZG+CYws@$9L3q( z3^Ow`RDF``Z&;6;o}Px*`}gU5YqIQldTaup_X}89SO7UQV)k-2Hwn|I7+ej;Cx9Dv zZ36@GeCmd!r6uz1HfvF@SO(SfKxm8n>v){qc1g91xW}ONn`m(l88%Y+=G!iNvG3<a z(I_K(I262@aDJUemHljwMj|+SFH<Yk-9|@WuvkE;!*PC@l4iQk`Xr7nMIsS@Pfw3k zsX$873%2tDs-05kmsx>~$Kz=RzAOLFtyTW0XRTMG>eZ-^6a4o-{`99(sbHQqd7okX z>ec=abW-jZ_r84aU_~?V&)YxSOC%D%u(uum%a_~1{QG>?<=eBN@*=bZ^<!DAca5 zu15%lpfWJVELLlp@2jHKC{(M(lJ4~TX#l8bV~oE}vzm<AVorB-_#Vl2G51ug8g=oK zkiO0J6|F{<eAH@HjY_jrXRYE#y|fy9pQSmkoa!|Gs7GW%S?yZVC>_*L{uN*VCO$Ad Tw}rU>00000NkvXXu0mjfi+6Y@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropdown-button-arrow.png b/Templates/BaseGame/game/tools/gui/images/dropdown-button-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..8c420ab85dad8b5ee3ca92464665ae071a471732 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%sd>6MhE&{2%E`$|abUXZ<1r^pL9db1gt1VxgHed#<;qq~ cW)1^}6XL9#0Z$iS1nOY$boFyt=akR{0H4Any8r+H literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropdown-textEdit.png b/Templates/BaseGame/game/tools/gui/images/dropdown-textEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..3966efbb56239be59ee13cddceab917b144882b9 GIT binary patch literal 390 zcmV;10eSw3P)<h;3K|Lk000e1NJLTq000dD000vR1^@s6OE-s?0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzC`m*?RCwB~Q&Db%Fbs7dAnmg~0N3Dm zSJ*M=fx<D$4K{82fFFLUonYM`QYUQ#(n2yPC~3mb)J}WBil1Z0c0z~*!0$THoUs+o zTa;U6$Tk~5n|1iXvy1>^?7l2ZX@QwNIJ_tdJyVq-O;hN*R;|Jf8oKt&?reAz#jswl zv@I};de2`VaYRw1b@G>k2y~N}rTzTSqF{8F^S*<Z-A?<`rSj&bDlUbDIG)P8JV*-L z)5SqT2%z-pOcKH{@PkI&X-T)75ICMrevlZ?j;?NaKmy?WIL|b~l5Tj2gY*6$F86K! zszt%*E`L89;Q8s%zg&Vf55q8(GwVrlf(POD^iWlm{?iSTB=Ld_Z<@vi-PLuyMVTHU kN}lKEM>pfDsBZxV07vonVb4jntpET307*qoM6N<$f}R+y&Hw-a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropslider_d.png b/Templates/BaseGame/game/tools/gui/images/dropslider_d.png new file mode 100644 index 0000000000000000000000000000000000000000..0c65347aa49bce5bb7ac3175bb4e7254a2e5c439 GIT binary patch literal 433 zcmV;i0Z#sjP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQ%OWYRCwB?l)-L-KoEw1cDJDI!5*}! z@CJqhkKmaL;cjp87Mwkp`T~$}wq7_%IQC(RSE|MsYn38N4TTLAon(^D?!RAVW_Lyy z4u=VDv-i|Zc3Qcvdo{)P{eBPxKKtE>qL11TN-0d!L<;JkL6p$?;pB>P1`$fQ+U<6W zX0!P(ljI{R`f|C#d>$Z<Ke^}mEJ!}0qQ`Lz$8q3!9-_s9t0|>MNjWu96hYGc@pufz z7+wYg$a1$jH9-(c-f6dC7)EwJ8jbKan_;zH|Iu6ok{S|pU5BD5nPxhjVz>L^f><6@ zwEg}Ywr!_>EJCl>%RrB<7FSydk|YtE%?7Hfo_;W%I_<1h)ivU{BudaU4Ph8U?mpu# zg%mZVh`@=65&@IRE0W_8NmAY0tg-{r%gNzzQMa&sYITkKYK{AcYsW}7lQNKHSvL>1 b9{~mc?Gaq0{#;i100000NkvXXu0mjfD>J)D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropslider_h.png b/Templates/BaseGame/game/tools/gui/images/dropslider_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2cb89d828f36ef133b7b15f0096785c10e4a72 GIT binary patch literal 431 zcmV;g0Z{&lP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQAtEWRCwB?RIzS?P!v6o2VHQgENw$V z82kt0;Oxd9Fu-E}!p(1hm4VcRsdOQve`3d09BM*~0!o3zO3H(_dXt;H+;iW_IrqV3 zbY0JJSv(i1lJaDl=GiQ+>yACobIGv~e}4R%gkcz{s)~YW%wFOt&reffYgKASQPhfo zMwC->wO-@Vd&2A6ii+1?iLJ~0nPC`~{_(I_V7Yvu_8f!I{2qhjIIt`W$8kUi!DKSQ z-R%u^UY&~i*L{peqYAfe8@})3eY2q|>LlWM4#VLPq9|59K@ecKOS-M?4~K@_B+X!1 zRy8YGmNA>nAP54r_nYXjZ-Q4mk2sE@C<^BDIeNVwn3mp}qJ0D83!=eb08P^>n^~5% zew>|((j>+Gbc*eEi=>!MS9B^WYe|~?>32C1Q55}pnUE%tBuVG4k!mMZM22Bpyx6`3 Z7yw+%?*7zSP`UsB002ovPDHLkV1hVF#EJj_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/dropslider_n.png b/Templates/BaseGame/game/tools/gui/images/dropslider_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a4577f9eaa14cd8f70164c0e7f6ea0a61e0ffe69 GIT binary patch literal 428 zcmV;d0aN~oP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzPDw;TRCwB?l+8|qKoH0Od=yiI?LkdL zEZzwZz_mASHa-By9{NJ%F?;|bdf?Ja4<wxJ5yca+$~P{UY85QBOft!4clNjcnb{1r zSS)gMvq$2$Y_Xcp=SNeP%jIOXTHUeRjgR-u!4Qg~pePCmXhL_%DbIhZ(g=Fnv{Op& z(*OF4=fN}|;JVMX=dBfa-Yaa|#=U9cX}#X7h^@<aYrS3%G))(sDc3B+Vm!V&9CUec z4oQ+oYA!6xDq%d&V>lfAU!yF`DqdWaWf_CP*G0#15QO2cn2Mq-t161)1cqV2_kD~; zBWRk2Goq+2b_bMJ4!IMKz+wo3fZ1$@(|(_P!ur^6P?~1dVsjisQG}|hq|gkKBx&wi zTJs9x2-E2ef*?Q`h3(p3r>NMAqPW%1-dm$4V;Hsy62<Fd$4EJoN|3JWod?^e00RJK Wa<!aicl}!c0000<MNUMnLSTXvJHAK& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/expand-toolbar_d.png b/Templates/BaseGame/game/tools/gui/images/expand-toolbar_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a3415c7d0845dd6c3455eee9cf904841813a1013 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^>_Du@!3HEnmv8(5q}Y<Y-CY>|gW!U_%O?XxI14-? ziy0WWg+Z8+Vb&Z8pdfpRr>`sfeO5sZOWB+ZZf>B^Ax{^_5RLOsFKy&%N|0!MxLu$p zMrAtFW<#m04k_jG@4FiJG^|^psZw6Pcw>fuiqzGzxrbKH$`Yv0>^=93=l;VFSHrgb z%`r<^aLoF<&H09{ECy126H<RRXU<yXD3PbqP`12z)hhWnwsI_q9V`npxO9CN&#)5x z8Mr4-FRA42T#3g|j{PhVx&Ot+zo)S1@q^!ww*A}3barQCpIdl(8rOwQYd$#vSvE7L zJx<yv(tkmo=iejWea9@i7>qa*UR$`HZ}`gX;%j)|py|{~pl=vFUHx3vIVCg!02_RG A(EtDd literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/expand-toolbar_h.png b/Templates/BaseGame/game/tools/gui/images/expand-toolbar_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c50608264ef3287c1f53766b1fdccb31d30c9a82 GIT binary patch literal 477 zcmV<30V4j1P)<h;3K|Lk000e1NJLTq000L7001Ef1^@s66{oTI00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vto}Iv^h{6xjd(0cuG^ zK~yNueUq_HltC1QzjN+903{ZyzJauc4zS;b*kLC%Phw4c2Q0UQK&&ygU`%w4s})c1 zkHww+_n(D0#Uzt6=iD>r-qHKh)5_!Z0Gt&h-X0%!$lpJ!oy+T+4@H0_B#q$i?lS~C z1(Co6RNIOWa?h+4geU-#I}p-ik-$$e1qFn+n3W2MA_;j*wpJviZ2VVti&<;RIt`g` zclRl8x65G2nirog@EWj_X^C*~bNP#}w_gYc;lL86$pS1Ei;<1MLO2rqv0U=`<{F1l z8q&xFxV*X=-5ZIRfQJW%d6vk2IskYvn{n{wh&oi1!HZ}$=kqz?plZnYkg$W7&u6^e z-){-2$qFLO$;rD>q1Kw&s<rIZ-d@)-u{IRO+BhESurcHo*M_R8p*{$Pp?_z`v{IH9 z-_y(yoQ|g33;~YlLACgDsD&m^k%k=Vb<K4;0N?NLKzb+wKAxX<U-0*8h2ZQTM~hL% T-dO<500000NkvXXu0mjfUOUmF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/expand-toolbar_n.png b/Templates/BaseGame/game/tools/gui/images/expand-toolbar_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c63504e8bf3133f4b1b93b750ce35f06909aab GIT binary patch literal 452 zcmV;#0XzPQP)<h;3K|Lk000e1NJLTq000L7001Ef1^@s66{oTI00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vto}ItI-kkt6^B0Z~ar zK~yNuZIZ!C13?f(U&5TkLlQ4~5)TFxA|n2nU*cCOh(fN3ie3T%lY<C?IdqkWnOU=D z8ya@EsModA(|dY+FuU~$+$7svUR}48@2{`H%j@%PvW+Ery+3hpe;?a}WSfxK(lB6~ zWP>HPRXcaGgd$qlYDl(`P0t-zRp-B%l1RqrUs@ZCNm3DaoGyX7enp}$n}}esSP&6S z%cd_2S=`;<Q=e=1EfFC;Rx94#-igTX87m_N0MqFdNl1ZIun_`awO%uy&xt5lm9({H zvl;bU-^yugjZeo!1kPAD&V$j2<CEj;tY_;Yo}HheGC!Yf;g5zRjt-9+ilyuf1(S;j zp}*N`_9-zO4qB=6HIbW{+hx^dcZiZm!i0A3wJ7`S#)>2xxdWT;tx*ToHVGSdII#=1 uD^PXfwz%1B!2V3KSuU3?@n`b`aPte4W_BJELSp~`0000<MNUMnLSTZGkGf(2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/folder.png b/Templates/BaseGame/game/tools/gui/images/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..571a904d00bf3c469eb0b5d146666f3e694c0ebe GIT binary patch literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X`aE46Ln>}1{rUgjo>{fw=^d`}7_}IKzB{QNCm4=xK7IG! z>mO-J9(OEvbUi%2d^>l;nbwL}2Bm-h{&gxUCO<gy;mxUqEDFYq)1T$Pk8|Mt@tNCs zQfF_k<FOO0>p$nejbt)>aDV>%C+tc`xc6(AJm7C-`}5m*BR88Bk4As1J+mzLe7U=- iw;9~LHXA!MFfx2lbli6~+`<s(G6qjqKbLh*2~7Yj)m=sa literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/folderUp.png b/Templates/BaseGame/game/tools/gui/images/folderUp.png new file mode 100644 index 0000000000000000000000000000000000000000..c188909a14cc8970c7f3f6ab04e29a54b49f7230 GIT binary patch literal 670 zcmV;P0%84$P)<h;3K|Lk000e1NJLTq000yK000vR1^@s6kc=(00000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!KuJVFRCwC7RLgGKKomWW9UH<!YJ}1S zNOjdnwZwvyAK(vyDj_w?vgC_crh8UdvY~<>C>vDNq~I!*k_93X;FO1Ldhf)xY=@+a zDi>W1%-nN0a~~sZw_EJ8X0!R3mMZxPx$bS7<bCpsMx*g%7fy;35)l)e8-{VB>pF6| z95R`VIGfMsm`o-xO%uc6kcsO=+<%ggRT05?u~-BV!m&4S90z`kBuS8E8M3m^gcuA4 z{0H^0+JKOi5!ts&rGmr5SJ<rAc+U1!71QY*y4^05e(;s<g<^!Vr^ptT75U9E9LpHS z`ultSRXJyd@gD2dN?bAQ<FScO=c`!5`y9&{#(G5z6jr$a_Qrms?E3mQzEi$>y`CsY z+!(`HuOpy%`<y@6j*W1UZf<Vz<MNW!b&yV{k)~?LyMi%{_1Y^?sB>$$UPalL%OytR zG1~1m)|9VOsf5=@M~~9M7_Y7LutH(3>jvNN{}AD5gcapU(=-$c1rS9fW|#R2O>SE1 zA_8oMrw$HKJv|l5n6%Yu1!rMr%mY?TQ9#(|gv*LdL5^^c8dXzqcJ>M7a#^GxnM?-f zzB9%vR-_Lvc$`#KqsedscgZFC)M_>4^LcUg@HsnU7|R^-N#QA>`F(@gY!=B&B9S1n z1YLb8l=_y<W{K<<Pm4*D@-{vxb)L<|!lJ5WBPuNGp2ln{R)3FUIF>PFAOHC{93Q_U z0!t{e?T3a}6h){_rBb-M`uY6h@FLIGi#)LZYkvh80NlT`f-uZ42><{907*qoM6N<$ Ef-KWFd;kCd literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/folderUp_d.png b/Templates/BaseGame/game/tools/gui/images/folderUp_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c329f0372480d652e56eff356bb7a88d52cb9f3c GIT binary patch literal 696 zcmV;p0!RIcP)<h;3K|Lk000e1NJLTq000yK000vR1^@s6kc=(00000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!T1iAfRCwC7RLyD{Q562>$5g8pL8`b= zd;+zrCgQ?32n82{h!u3}#)Z$2;tMn`Qt%0~ic1BNBn@WXph3H+f?)hf)p4BOGtNv$ zYtu!^fsf1FbG~oRJ@=f;sj7-YRxB1jvgMQ^#t_!G9~dNt%UZ4W<q(hC7i2;ta1oEk zW6Tf=h2ZsiHQ#77La*0@BuVIWIwYKD!qQ&_DXU;Gc#%vdaeVw9^Z6W$#RBW~TJwvd z2)o@5r_+h+>l-wiP4xTyyfs@>kTPRp_t|U~KA#Wc@fgpU$K%0pI7Gc(C((CCro1Ia z%gpchVLF{a5cnOeR?D4lilJEI5Z4gljiXY{PEJlRpUolg!VU&M2DrLvXsdOfVu?ds z{SwU#Pw4{ovmns1Zf<W;sZ=n2d_0&rarKUXs0+sEHkGvkp8eUd0TcUrcZYJhj6fg& zm&>)|CnFAV_4YC|3~yUQWdu%xEX(M2yC{`Pn9XL$WHRvk-kN>`#L?R-Y|U_6Q2zHF zE!j~TjRq_J6mGX0(P$JL^IKyMnc;n)rsV|z9LH&R!$dxvogtM<X`;DY4vMld{RW6b zTz!g6L5U_n6ht$Sn%L=ljq~%*NT<`7OeSzR9DfKBhq(Gknu3=!PNGf3ehFS39idvS zA`*#cg8N24CN61c8#F0|LZNWqYPD2ZUa3#>*$x&zpVZ}Ysjb$1iX{%?elv8e70z#G z^E+%TE30ib8`d7Owi-q;6iXatAO86`BoYZtL{StNF;p{wU4kIm&~CS%e;i)q`FfED e_J8e{00RJAYhCu~?Z00D0000<MNUMnLSTaAIyU71 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/folderUp_h.png b/Templates/BaseGame/game/tools/gui/images/folderUp_h.png new file mode 100644 index 0000000000000000000000000000000000000000..953fe8b40c39b94f7abd549c88044b679550597a GIT binary patch literal 696 zcmV;p0!RIcP)<h;3K|Lk000e1NJLTq000yK000vR1^@s6kc=(00000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!T1iAfRCwC7R84CdQ4~FsnT%i6g+jWi zVnO$o1{$mj5p)~+1BAM8l`dR0zrbJMMo>t>Uywz*b|I2VTBm_#8_`0VqAo)Ho*Db@ z%giu-BwdtTIlS?`dk*KlckdIH%O$o&KA(TjhC_xJL)e(^FenVCl}hE)HlDC9$b?AX zBp#2)SRfP%ArJ`gwZUM3Znq0XQP6BQNjS%Zl?MgMRWKMlNhXs>Boffn1vE{AnIi}S zL{Wq&?V{0WpkA+|*X!l%ZY@D_#>Cp$Y!<=b3oPbyJQlmM%*1}8R;!WdE7R$YEr&aM zNbJvOwDO;FD3>_IHRpHCt<sq#5^ph|&G;7WxW4Y-?Cdii?emmN9O4?6XgNHj3s4v8 zhBMmMw{{!VY8Cx{e`B3E#5F1ck})5n+f+3b_Dj0C`GxP7mnfIZ@caFE=JjH|Du_c| zqr5DKVJZ#XxKYA%I>nExE0jtlOePa#G8sJI+uJw};uvKWwmamZ>$>$lo=nkdwJ;u! z;dZ+bjYh#FR&ut5>99jZ%c_VGmK+`?^5N(RsZ@%K7K=q|%?{$+b9on8f(mtjD2Xgd z;{bCbJ7gRme?&T+#)y@T%jL4xO%R8;MoU_Pr_@fe>|mXtThJK<_A3+$h(scAI9AsQ z#33$etal1^No;@rHSX^2R?fxgbTTm~X0s{3EHivQA1*GwQnz$ir@UP6lpOWu?d@-P zy}lI}qtO_{;gFB!9OY0haag?j=i_j2@P?loIa1a8f|n$TyY+ZHI6wdL_~Y;-&)1VY eu>Tu>1sDKTPO`S%%A^1Q0000<MNUMnLSTX~Wj*@< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/group-border.png b/Templates/BaseGame/game/tools/gui/images/group-border.png new file mode 100644 index 0000000000000000000000000000000000000000..61234ae1fb3dee29eda38a371165d36b060ee426 GIT binary patch literal 1273 zcmV<V1P1$wP)<h;3K|Lk000e1NJLTq001fg001fo1^@s6#ly*40000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$n@L1LRCwC#SvzPIK@^>Rq)0SqBM&U{ zK~RE8k;)fTv@rNVFsUphN-&N10jmTOk|LETicKV_m54TpVAK!`|L<7|`4E(7K4ARB zDus5>J(HaE?!4W7v&qH}czMI@+&44#&Ye5=o}G2_O1ZzkpGs6d@8oj17WcW!E!8%I zG7SwXm(AMBn08BdCX>0T948-RoKK$0X0t6*Q&aiH#l^fexw~LXm>2UyPMmS3yc8^% zPN&txvs=#V2iKgriKmXs^9tJFqjWWIBOj#Opr!-61IK}Xx1-s?{L4G0_&?_ilXv4x zqfZ_btu8MwSK|-wsQY~v)cnk(&+H?8J(|C{sz$fn!M^&){#~l$>@n5f-deC_;MX2J zsxZHFtA)8SUGF$_vOr$N_B~qGz~m2~oD@4WwsorNs!FYtyCThVSEZ(BX4T=g^Lm1w z!FRq%(Y|`AS3jc<Cv~l&6_5**^nUE6$45<haxCVR@=D}Nj$a<p%0U1uOeL}MFv2(# z2tZ${WcR?^$Z@ikN(RYGnG7hakE(@|L4s|Rj2aszuRu{)lmx5FZHXdSgCyM`&REk8 z0u@DP>65Sso~f)<#1w-6X-QZF0;z1&IJw>OAO*=w$<u9aw^a7jxpvjrwAD8lZSX;I zyD1QJnWq}GrEaI@2l*t&n({nFPh5Gd-h59NoYL0bejO_A4ZrY|mvwVZsn^ezHdWMl zi#O##dGJknCDJ-deNmOQdkZGUfAxkn<>5nIM949%2g^Yzljy(oWy)Hf<EFN}n}?Zc zLapFC#cs|9Mu?HF^DdrP%GqLE9C^+^gPwq9Xi@7?hD9wb^UI<uhIxYMYeA<p%Ga1q zK^r%$SL@13)%@pg+Skx0PVf&S@6;o9F$W`D;8Zduf{z#*WH~5^XfZqf1Q<?@z8X>= zKP~7s_`nZIkS{YqF~l(je$;4#Z=xBAo(3O1_wqgCBii5tJte9r;~AA?zGq4`(Ts_n zWQL)#a^A!gm6r6qM+nP?0Sc)kNm@4Xtw-=SGYrxowl6GaQ(5@5B&1Y;X$f*x_i`5k zxlG?JMp&eYe3Cfrf#}H=#@X;~|BtO`5XzBM3_XQWv3X-F3LkUd8np$mW$;ZD@}tlb z@grOWzKMePX$!hz`6y0z95fS#p0K!L8Eqx>`_a=%75$H%65feFik{-gOB_87-@a&t zo=kbNraT|vFoAMIeOeI8_0#*bHBgfWlms3M{F=|;vk&HAk-V?7qf70j+dSQteXuCB zxme0WXRY7zZQiQU6W{Gki*a6ad*$6z80sP_3%q+`TtsI83dY1J6eRh$8vuYoz-JP{ zd_mbs-aVmUVdndNBq%XE#kqTuz$8gP5_<Q<Lj%j8Z38TzAeF>U;MoS^Nw<l*Ix0X< zunJEqYTTO=92dLblEiH<HjY;~oP)a=diNy#h{{q)8}uXvQc2E>xEOTzWZpMPmn%SJ z?(zSC4+0292nh@kvId`u$uPqZCPNED8bgi@LmbC`Ql1P|ln1?_pHA~@{Z%BXuPKfW j%4_?bDUt6}KLr>7B{DrC{G$C100000NkvXXu0mjfaJE^D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconAccept.png b/Templates/BaseGame/game/tools/gui/images/iconAccept.png new file mode 100644 index 0000000000000000000000000000000000000000..a24d6052d0091ca884fba0db05ae610c925e5030 GIT binary patch literal 917 zcmV;G18V$<P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#CrLy>RCwBA z{Qv(y10?_;fLPE4l217*|M~Mf>hIrwaX)|mkofoS4+F!$e=ivRF)aG?_wTZe-q-IU zs|N@mMr0Qxp0Er4`TOT&X$ECUeNkHmZdQJ<_~&0A7*0Ok&2Zz>`R71`a`%QkSPRnw z5I}GP;*Z$`fB*e+p*5d3x4N(a!?W*?8J>N63YL@Nl4g+Llwr8`{1n6Tt4qHEogIHN z=@HlvfB*s;5PR54>F1x{hph#?rBsFW8IHc*&+zBpUm*U+zywkR@+%`VgSD6=!_}v! z7?xaG_6+C}+e_Jx?*Rl5NS@*EpWiXkjLK3Vn?ag?{Q1rB>+f%dKS2EJ&o2gGm@v3X zc{7}PahO3z+Kj<aSV!vTZ-y9<UVs1s8}R$@&sakVJBBCU9sxD|VfYISxPL&J5$FQO zfBzZ$6~Y)CWjq-&bW0eXe7etIuj<V3>nB4jNH0JDferZn^QW``n-Ig(uMdHm|1x+; z`!Hl^6#_LgF$5@vGuTVJfeqb!XC=ea5BC@ZImH<Meg7{F(hCqkEKma&{sF_{KhQ>A zW?lv>2?qu~HhzX$vknF!4iT{CSr>X4Zoj$Az{AP|3=?3$|Axc@KmdVV!0-oX@VD0t z(p<6(Uw(dMn0BF);p^|uV9o#j|6`bSuAAZdtMlN%l;V?Pc>Uoy&~*&pC;|u|NErP7 zzxe3A-3(HkvJ9;M7#LoCea0~HbTh-Pw^teFobP40_WA-)10w?y5X<r@GHkiBivj3} z#UQ-^0R%SS=lB0hPCvWw?DEqS43<(Z3~WG!@4h}~n0}^};o6Jy49x$T7}%JY8SIrj z8BX6j%y8k_&1Ve17?yza0t65|A?Zw#4gUJ`|H5RGAZ{aBYlg=k?*sjH9~>?+f=UcB zeDVw@?;T`VuyxB<CRWDy2YpY#2?-#8P?EAzw`}mgpZ_Nt%c@IyXt@GITLk1(hSy(T zFl@W9i{aAsyU!V!7;_)=KY=G@fB<4d&y5OAvdRp9|3?7>Htzq={}K#;fhm!Z@dZ$7 r5isPJJ(&CiB{Km85F@4886dy_xpiI^gI>TZ00000NkvXXu0mjfoPMTo literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconAdd.png b/Templates/BaseGame/game/tools/gui/images/iconAdd.png new file mode 100644 index 0000000000000000000000000000000000000000..323edb0294f8c2654dd23e2f585f6d2b804574eb GIT binary patch literal 847 zcmV-V1F-ywP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!;Ymb6RCwBA z{Qv(y10?_;fLPE4diInm|NZwj>d)W5aew~)k@)lPFT?M@e_jCji-7d9iQ$XyBC7`o zAVy>t^z11K{`2?WWLYs~Np%T*22NHUhF|}FF}(WnjN$m5eGISOyng=Y&!5~`ajVzD z^Z*19+yIE?zY9%N?73CNbQ$h`y2bF~`%{MB|9&&@v+*;CaY{2By|t6!>YeLffi8<* zlCl|W2tWXV4d~uoq6BpIVFM*ADOGVjhJ(*{F?{>_jp6tIKMWik><s^b@aMmu44Qm8 z499NnVYqq!?z7*2{@AX|-hB@sfS5q005!)5@QX?*h-fezd9jz_`|lqN-+q5*C^W8S z$kZ-j`0(>R!@G|!7>++bz+j=~$iT?<R|*&&F(ADF0R%Px==4~5F?EI;AFnXH`Sgb2 z)3;9yAHKeaht1Rbj~IUc{L1k4^IL`|pYAhQt6PHtHWnlY5J2Eq0CJ=`Sh*M;e!dS3 zw4V%>X00eOGc|Y#m@eEI#qjjQJq8_N6R;kT7(f6)UGRtD>+jDDpMQN~_|5zqZ@mBi z`;UPU7$!giz<huJg1O-7)AtWmc-VOuK74=2P<SYX;p?kU46{O3!EFZH#>m7V#V5z` z=+%9YrB6X(00G1VHDK|D`zIOXxD*-w|NhCq2sB+tkQ>G3e}DfoF#Tg>kmXZk*miL* zSPw`XAb?;lSn}@|bNb0U2c#^OTo{f%+`;hY-yg8e|Nnv=&cMdR%wVtN$*|+X7KV?X zzdQp5JUCzg0tlXvk`@F36VjiB(h}m_w(2enk3ZfA2H1Uu|Ns6m$OtMi$neQCY`?gf z;pFuzUxCRx{(QqVa6$qIAe5vWIm;^;=$grYng2-IYuYe~^GSj%XL$Vn5yQ^&`xrib z`Su*BITxlG1ONhv5j{8hPj*rU8WQyzo|*mtz4#ZLng0I!v+R7sb(G8m5I~HSW@mr^ Z0|0P}e*vP29Pt1E002ovPDHLkV1l7;l$8Jg literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconCancel.png b/Templates/BaseGame/game/tools/gui/images/iconCancel.png new file mode 100644 index 0000000000000000000000000000000000000000..744df795af9996f6c1007a592dad168a5e5c5a2d GIT binary patch literal 853 zcmV-b1FHOqP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!=Sf6CRCwBA z{Qv(y10?_;fLPE4p0+l`G5q_R`Ty@feTKh(zyJUHhvEO<znnmPk>Ssu>GBh%Ekafg z5I~H`E&ytdXXWEsAYi1)z{=0V@c;K828O@?7#M+azuvxQczfgw!_QZ5k`!kym=Dtf z5I}GPp0?B{vhZ@v6ExOhVEguzf#KC728MtC7#Ki?{QSwlz%R-0lUIh}^}!PiKVQF1 zR$jDn9!MWR0D%p7+FX~&!UxoB1l0T$sQKAF2B33+*8T<>@Ec;tuU`xd;tC8u1>_iB z9yktiNuuiNO$z`5hzVp0(4U!thMEj)A0IO?Ji5)mASlDYpyLSC_?Lm<JJ5DbD+UH3 zX$FQ{R~Xoz++Yy1Gy;0>S0+d=Kmb7k@y}m9R&H(vhHF5Ze*$gd;Add4@?&7&5@cZb z_8G|bVqjP`1t@=sf#LR526kmVpaH-2L0SO<2yDRrKfk~I|M`=h5oF^JppEAaGBB`k zGBCJ?Lqh7{Mh1q%J0Nn5Obq{i`~n&94I~8+Kwty@f&2?|?)P61O|0yQxMBqw_U9km z0HBLOmV%`K0tn)Q-@myT8Gy0z?Hj}pV|xY$hX4kKU27N^K7U|f2u=Y8EHGA9%wzy5 zV`2p9`NIv;3lKn%F!=rZ^6$6rjoE~O0d?mZ1H-)=3=E4VFfi=f1hfrk)2rveczz6t zP9-gdAFtkkTyPnr6(E41Uidxz)sfS4L{0S>*uQ;eV7LjCyLJ&0jUeysSPwSnkFplS zPbD3OXS??S4fs19q!%E7;0ft&OlS%#FVAc-Gb09$`&WQ20lMP<KX7XN{qqOIKRIQF zU%=3Rxo1Bx4ZKd(Idf$WNFP7|p(N#7!9I!X{JitT%#0b>1o#>L0u#@_e}BON_x;UV zhBx~UGW>l0GD+v`Rd`Yc2p~rE+<4Q=DenKDKfuiNS06}!XFz18OF%qb_w2QW$m#(C fh>_Cl3=m)dj8|3`UT=(b00000NkvXXu0mjfz<zua literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconCollada.png b/Templates/BaseGame/game/tools/gui/images/iconCollada.png new file mode 100644 index 0000000000000000000000000000000000000000..0f40170142b2faf9530a9f262c7493c8785f54f8 GIT binary patch literal 477 zcmV<30V4j1P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUze@R3^RCwB)Q$0+=Koowh<xc~mQv)N5 zCagHJl*p)VPK~o+!3}q1bkR5(opi$4$mAv~2`-K>3PBJADc<+!)z&~zUUIqi?tS0; z-p?tzt{)K5nOIT!pS8rwD}tc80N5eXW`Q^2OB4yJp0nrI;cvI7=KkAC_-Hrfr%cjM z;gB*l<V$@-H&1<ftPbeCHzuSo#>MIW3hku=>{J$)<?4{?Z<Yg$ud9?wXbw5<GAAu! z1-Wd9!C)`kb$FL)Y{n)c0Vq|vR1AGkQ3Nvt5sYua3_0dyn|HTdHyKnmnil1*I%KAU ze9lz@sMh%MfT!hteA}hino6akRiWndhv597$5Ihxo?QrE_{n;yGC#=AZBJ@3s{^uA zu!!x$1dxQDBRBLKl{V~!>bt>(on`*6*|pJRi>?pD48E@nSdknWfUu0D@pK&xRq z2XHXc0mosQp}-g(43X@NLojcI+kMLea+_wqs;IkNM58}HCWOk{B(%tL{uW>Wd*h;H TJdxWy00000NkvXXu0mjf(#6;d literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconDelete.png b/Templates/BaseGame/game/tools/gui/images/iconDelete.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba9615b434d3d2432db7b4802fdfe37a8bdfb47 GIT binary patch literal 846 zcmV-U1F`&xP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!;7LS5RCwBA z{Qv(y10?_;fLPE49_QsL|Nr|h>JP);xc|R?OZ;Q_$MFCEpBH}_{w@0V|L-#Gxy$b& zs|N@mMr0Q}$;}J?^Y8CuK1~%#ZUX}bMgcwsh95r|e!qOi@NUl@hWGE@KL7jwZ?55@ z^=n~z00IbZ07&!Szke4Bxj1lh>+3Q6y?2d);pJlnhF`xJ7<mL480F*`-fr2(@c6-< zumAr3i8o!o9c&0d0D%p7l$)yrboOCkJ4-1p6GMi-`!_Ru`~HK0@$YX2b~aW9#()1A z{{Q;Hz@TNs@OsB?h9@r{J^T0nkFC|pefIzYhzVp0P;(5Il&}=1t`@`JL)#ht{`}7H z@8>rL)^DE}{(X4M@b}X@hJQc4G5kKfn?cmkk%9BaPpQBE{>Fgx0t65^5dSg$jpf$S zVEA|a0>iiWZy5euhJ@2Mv;bz4;Ai-A|1N{1ffd7tD~Do%LQMbx1U3K|Fw!hsTnr4i zZZR<Y{K+7^U^Uh#dllu!!20<&1BbB%!@moEq(Nc;0R-{Fuip&+J^`Kk`6C0{-yeA6 z{m-9&Kn);Q{{iy>0tn`Ur{5nwP~qg}WBB{%E!ZZkfQgBLO;(=a<Nf;#|9<~@3K9Yc zAg~Mm{`<4|?Wt1?|D{wI{(k()z{<;tq4zH^w*LNSW?)rNVR&?PKLaop7K6kA0tjpX zkhA0qH^;NL2lg|tdUyk4fQf;TnHi#yfsx_wUyz$w82CJV7#{B3%JAjaw`YGD|1SZ_ z0|XE}A)Sl%3kDjrP?SfQTh!5&;n#z^48QK)W%&2&9|OC(3Im&rBE#don;9Owz4i4U z<G*;XE!V&a2_S$_lJdz&_h4Wuo6Pq2m!yn_B{15gAkq2k5yRc9hZw&7{qY>AITxlG z1ONhv5j{5^33X8Z|L1qqALf5?|Ns4!0L2iH{Q}5d^zYA~WnSBFpkyY10Ai#xI|Bq5 Y0K;`$V5?)dQUCw|07*qoM6N<$g1{!4X#fBK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconIcon.png b/Templates/BaseGame/game/tools/gui/images/iconIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..e5c83d94285b577c37382d56d2f01fddba6e5d5f GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^RlY zAw%ze4>_O+XMsm#F#`j)FbFd;%$g$s6l5>)^mS#w$tWT!D>`}6^I)KmWQl7;iF1B# zZfaf$gL6@8Vo7R>LV0FMhJw4NZ$Nk>pEyuaw5N+>h{frx!54*qj?muz|Nm2)3*6V{ zE}9;5DaUz<gw^KM^M}LJ%8f(V&2F%Di*h_yYuj}_D%8crqi%ACsKoJ5ZnaO<TxVF< sFOTWi^5eBr)`72+*VQds#d3-9z(*OK8}fbJKx-I0UHx3vIVCg!07x@ZI{*Lx literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconInformation.png b/Templates/BaseGame/game/tools/gui/images/iconInformation.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e4722324ba066a550415999190735f0b8feef8 GIT binary patch literal 918 zcmV;H18Mw;P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#C`m*?RCwBA z{Qv(y10?_;fLPE4f@j`T{{8Py)c-&K;(q@6E%E37KZgGd|6VXK{9E+*&%b4dDy;4z zs|N@mMr0QR%(x!>>(}qeiv0g2&Ey3b_}G}i;vc{NVK{vICBx-sU!OBD{?9$t=)4xD z2Oxmp2KY_C8vN_~?}e_aT--X+ybMp@{$hCX@jJucfBzZyxtJNG_&FGEzxm9te(&9{ z3@i-srzZG;4FL!sumQePE-U^1{pYZ&3b&NH1TVv>`yUwo{Q1qGFE7Zz0F*y>|2+dE z6C;DRBoD*m_n#Ry?7IDo;r~C|Q?tVE0R#{e$P}QTV-)%ROKC{*F&w}Bj^Wq$Ukty0 z{$ubpk!0{Sm16h}H0;;UUkq2Dd|*%z;b%}&;gkCR{dWvVFF*i+4fyr<Z>+h3FvI;9 zUl@M>{tYznFVN|K7}gzs#IP2KfBpW&@axYnhA-d0Gdy_xiNVG|o`Lb#uUL>?fB*u= z0?<ZjL0%Sy+s}bc|NEEW_ut<@7ye_2aZq9ah0yWq&%k^J21bS#Z$B}pNy#w$|NC7U zq!%Cn3FLMH00@Op+kd_WXoCKtZuU5l;D?|h{sqiCoMqhMp-ss~=Tg<WwF1s1Ps>4h zB82DR3Lqv(Wc+>l_UjJ@VQyAnEHQx%0~rE|q5nY5e;ED&z4j026@G4ZhL@i{GcYp# zdkWGC5I_(Ee*IZ|=;{*&c~Nd)dI35W=#)SU1qMwyK?W`kR)*X_eQ?<P0b)f7eul$` z?|=+m4AKe^K#-L9<NK29H$JB;OA1JtsfjWi0UEaI;7x|*`>rwk2ipE0oErZ#7^=%M zJoxa5;ojBT&w##I0@4Z)K=6cQn!F<T|K}eIy@GVOl|*<MZajR=@cP|nhJSzlF$f88 zF{sK412unP*tcf?R|ZDb_*3i4zzGQ;fKZaMY50O*hX21ND{D(i+L$Xd2=lOk!{F7& z9}I_%-DG%l_4ac{MyA|T>&xLu86bcd(Q~6&=nUom|Nlk-Lo$xx-wz2O4Gb_wU}j=i s#K`b_*~#_QD47W$fEX#w&Hw=h0H{BLdxllh{r~^~07*qoM6N<$g3ZFYTmS$7 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconList.png b/Templates/BaseGame/game/tools/gui/images/iconList.png new file mode 100644 index 0000000000000000000000000000000000000000..39b80d2381c6e2e6a7b8bf6554441ff2109f7418 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6U4S$Y z{B+)352QE?JR*x37`TN&n2}-D90{Nxdx@v7EBj4G5m8BHi<0f<fI^Zbt`Q~9`MJ5N zc_j?aMX8A;sVNHOnI#zt?w-B@;f;LaKt-0GE{-7<r;`&F2pBlM{Qu-tdgI1_|JBd( zxVd%Q)w#jK$(V9FqQFytdxi$@kwE>Psz$AuotG367#P<5mQ}nYqd5a;2!p4qpUXO@ GgeCwJlRn`9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconLocked.png b/Templates/BaseGame/game/tools/gui/images/iconLocked.png new file mode 100644 index 0000000000000000000000000000000000000000..2e72d2fb2cedee515e82421e8e685c44f110515c GIT binary patch literal 813 zcmV+|1JeA7P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XU z000XU0RWnu7ytkO1ZP1_K>z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;qe(<TRCwBCk-clARTRd5_nn!%*=%MrOxTbFB-xO# zf*@kG=@ilkHY!$IT1X&`1{>4Zn8y7BY^^4Z7Gh^1$bJ;U>gtkcW}TT0VHYwniN5po z-h0nE7UP&fpXzY#y+5Akxz5bU^PG|P_V%XD>?09b7m=!nAR=cX@*u|e+1A!pXLL7B zQ`|V9R;#^YW?wBVEZiuU%OpvHs*>k9?RJ}1tJM^dx3{;qfBGMwR;x{`>W?ccE34jn zj*pM&cDslO#bS}Uxj6t14i27($jduBJN-0GNk(>5b#s1xel>(Zy<Tr*S+*QPm<S<E zWLdU+czF2Cd(YzH;;M*jjouYULyU2KYHEr`qd^Ga&CSitr(Ejk?(Xi!(b3WV($W$! z#`V!bG6uMwBnerT5o2ufABjk_+wGDh2_kZRw0CC4;}74rhx5yv7E?rojo<f4e88e$ zK4Lx+MELUNi>NBIfBwbm`*m*J`_2s`sPWduYdDwS(n*|q1I*#3CczBz0qJ$P+3SHs z0QR>(x$HpWaM}CzYf6<Wg~_XslyH@6po*Pm3|bBHljmsOXXf^Mn3u~AEC?nB-g9>M z5oh;*hQ!gl^Eu5gz5zer;!%T(gXieLV<M=JmkulsL#rWDeRY*Wr3xq_#%~h|OVLwV zTcCg9&|Yuo>L0++fsdF7_`un{$NcgAL$HWyfjkf3J?Fo)xOj4ciD05wlraZ`0II0> zR9<?4;%pT(u=c^bta-tnwVAmwOR23`uMc8aw6PNN5g>5>L!CnT3S7I2OG^+VcG{tT zdV%lsn4T?RC>q8)2oY1I{LXD=3I*Kc1nx=-r4mHJx&tKZ5C{1%su~t`4B&-dKmPRJ r->@KPh*%tEHBk^l$7a(Z;O_wd-;v4;mcsSO00000NkvXXu0mjf6$yNA literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconNew.png b/Templates/BaseGame/game/tools/gui/images/iconNew.png new file mode 100644 index 0000000000000000000000000000000000000000..2446ae662fdacf6098e97ec7bc9accb9c580ccb6 GIT binary patch literal 611 zcmV-p0-XJcP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz^+`lQRCwBA z{Qv(y10?_;fS4F41ONfV0%QDtu>Jp^&u<w1|Nq7C|IcrR|9^q_4~YE}$o|Q|@cReD z-#<Tq{O@2kf6yXEkUoF_LKyJ(4+E=C1cnVDAA(gf!UPx?-mi{?%L4=u+<>1So-%L( zRsa9@9U{O8()<t1_|Jeafsu)w;s5XNa037W2;qW1e;EG#dcpAL^GODVe?P%q0n`6L zG|=f_{GEYKB#`0X?{9De00Icf3qXG|`~fQjS^?FBtQmyAfox*<{|n&)fB=FU@aM~0 zh?7xl{?70ZWFXXb2C(;l2K@O7Hvk}j;0FBr`-9=f?ZXUze%@hV`1=cN)88MT7&zp` zAU1;x`3p1vXbEx{00a=+fWP10GW_}Ul!3<}8e|+WJRUN9KED#E3FPc=K>7#71t5cw z3;+lqctT<X#=!4aml)pfDPj2g?KaTF-wgjiVFA|+amBy?e^Fcj5I{`G@y%-%&+z-h zYlaVp`k|VCz%_#d@job_|3VTI6aWMe+zSlARL026#K3Ko%<%o%Hipl~HZd>@>oagj z*a1EC9~4dC00emtlsaJmAb=QQnd$WszyJS!14AE_T!Bvf^Zg~*flQ2yU@?YYKfy5q xPTGI|GAOkD2d6B60Ai#xHv$9@#kmn6zyP_oI$SK<HXZ-~002ovPDHLkV1lE;4T}H( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconOpen.png b/Templates/BaseGame/game/tools/gui/images/iconOpen.png new file mode 100644 index 0000000000000000000000000000000000000000..29e80d77a85ee0d600ca2adef515905de855c1c4 GIT binary patch literal 873 zcmV-v1D5=WP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!`$<GWRCwBA z{Qv(y12!=4$m{<<|NLS2^XDI!f9vVnZ;b!`Gk*H<=gH&8KbHJ@_pbZ!4Bzhn0mO*Q zfa&|6{m*ceVtD=K9|I$h{|~6{_uqdEEX)iH3r~JvID6{pmKRT+Mgjy73j+@D_wRp( z_uu|A+<E<<fr$yI8R(Kv-~TY!t8p^es0%P~voZN>-Sed#Ab^-~831w@Gtg=dpcQOD z1_*PoF)_UQ@SQ<jkbxn=oF8Zidp1A-;WPjQ7=Z?`0-ekPq**`)urM*)d&kId<law) zkKg|@umbr20mO1^tLX&}J}oUqMkW;d|1-1#{m#h1%mfu@{Lk?3AJARw%nXc7KyhYf zuqy!qh=uV#lZLF#d{#z~b^oD`2eEfQV}OJ|$e4d%@2~-F1co}$AfOmf4JiBp0*Hm- z&;K6`|9-Lmd@-Hj?=PTxnfQQ`ybK3V$uT6@N;3fUFfajK2eKSw2ouOuHU<VxVAz0S z5Fmh97=AM{{r~q1sF{u7KTy@zdq){Q-nhunA;7@!axMcn3jZ@eXl5wp07YxkF(ARn z01!YdKsRyx`}3XQ)4e+kzu!G&`1kt<gP>^)1B+HB10OfgpZ}Q<;Q}-X6eM@2nKS(S z^M!%&9}^=$00CXV#K6KS#vtL?0WuflE}+tX4BuWXVEFYJq>T}32qT!y!e<ROjt%J5 zfB*md0tg_W0YIPp{lW0-<vgfk8Ndd8{m#Vj>kk9N&)*EdMD&;82axmq=Pw2U@%Id% z)ECxp;w3*fD<?nzu`v7uTJY}=)U*E~uK4$xfsY-ilMN^dG@RkzSB8IoKQa9O^O1o= z%8lVZP}S~kD+!QQ00G3p@azA7Muxu-&Hq94FJNH)1Zw&L6#5Pn`Ub>bfSQ2@{QV5E z00{sD5X=8x|5*Nfd&}_e4=YeJ(69eN0S9FNV+R_<4dnj@@_zwc2*gZ(82<kFfnoqa z0I~f4@$=ff8G&l>xWWg(YW9M%<}H{IK!5=NX(2o*YEegE00000NkvXXu0mjf&svoL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconRefresh.png b/Templates/BaseGame/game/tools/gui/images/iconRefresh.png new file mode 100644 index 0000000000000000000000000000000000000000..cce137ba5397506270226462ee9aed3d322ecb61 GIT binary patch literal 789 zcmV+w1M2*VP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!r%6OXRCwBA z`2YVu10fu~*smgRwkI<-Ie-9SVju$i`1UjE$G0CdvB?1h5F^MG5Xe86BKzm}pWxp= z|G5A9`P=yS&tIZHfBa_n_2U<Vw2(BzuV23zZrr&!@o3}e0%QXK0*D1f<n2$k{Qdh+ zxipuwKR>?!0~Z@N0}~@7!|#8;8Giry1Ezoc{>kv^^XGisJdH1xC*7!l82}JKEV+9V zWq<$rQ?4$o=`Y4D#_;9W7lwcT|1tdh^BZaa&=o-X*I%F^KYlU*UB!m%0)PNw0SW|5 za!dIOa|(lP`TFxa!;J?w7;fLc4Rro*poU)zGE&kEKo>IHx^a8T?K$_$kPQF`AeNus zf4Xz?a|3Pu%JB8aH-=rucRd37d(FX`6U8t^8tE#RfZ=oY&b$Y?=z$0jKrG+C{WLsw z?f6firhh-b|N0K37aguYRg0_$7^o}%{`uPxza}&h<f32Dzy+}Z0*K|u*B_M-!C!DE zmmJSv25PA2x1HSzGT`2VhhREvk)IM!T^0~?0xe?#(jNc<2p)~d02rn7flfA_>asu& zCKfW^rx&Op#L>V}j**#(;rXlQ40{jn-vJOnEa;knQ94<IQ!?QX(;o()lmEk<EF~z# zprES2z{t!9jFDdq51u?^0EXXAfB-@<02rlR;vAAWN&?CZfB*hvkP}c~_zjFcFa}2D zryrjfn18b{+<S1J;mVb38-dZk79fC73;;&yub+&+7=HZv!SMIrKXCkmG=tK>x9?vW zUcZ0M@bu|3h8x#zZUjbaCooDM0|XF87(^`bpCHaFkq=6`cOKq>hBnZkpTAxMW8*9^ zsqX}a&st!VJ_e}=2q3Jv&vT;V9H$QJOT^?<fB?dp8^3@3kq*p(%ZSOT009O7rn%iZ TcSC8)00000NkvXXu0mjf5>;j_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconSave.png b/Templates/BaseGame/game/tools/gui/images/iconSave.png new file mode 100644 index 0000000000000000000000000000000000000000..50e70bd70532cf4ddccded3d9f954c85cb195888 GIT binary patch literal 752 zcmV<M0uTL(P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!f=NU{RCwBA zG|S#o$iToL55zV=tOUe73=IESfHWh+e+DpMW@2Pu0^<Mw|1tdg$MAuTmEq3!pT9d# zP4ip>5I`V>TC+>7Qb6j}rP;xH{{Q>W@aqo{|NaNo%*oEgz{tqJ@a6kIhA%(<^51*@ z#bDviyEDwPw($c55ED?nElAh;Q=b@s`hd3lXULm;8fX)c1JuRJ0<j%r;CG;bfBrEr z{$*fdiFa2P=45A^3J^db10+DUf;9XF%KrWXarbW^pM{wLYye2^`!A>*(1HV(KQpkg zGBbSt`G*Z4fIwd0`tuL$aIhi}U;?@KKS(ps=|K1cX!G}<|A8+24|XMpe)jeU$S_8L z0Ac|J*k7Ok5Hb9O+FqMr0M-md48Q;WXZZFL<aBTV0?h@&e<0WV1qUiX0D(jM-(Lo> z)0!8Y2YMUmXGWOq3=AOG{Q-In8X;gHARq)ZfEla@Ab?mHz)k|0{+B^aTpAP^koW`Z zfI1my<NyCqFMyl@wB+~iKcMJh00<zE3mE?X1setijBo}6#L535;RB)mgXKX^|NR@1 zE&u`uY`~wt{}`ZFg8c{zI0mpnh=0NU2HXA@oJN2whM&LxgIxd+K%hWm`1=py<hn>1 z24X<z?5n_Vfw%-9fLK6L_7@hqKtm=TOTnu-&vG`<%YVQL3#0)cfLI`Y{SOXt5U`So zCFp{GKpX%51}8rTfB<3v`t3i^3yiQZIQC>QUd<-5c0d>W1*Z>?UVs2%0b2gx*Pnli z#>!ma3{zk|hoB3@d4VqY3%2k*KmakCW$o$*8l(nvfhEvLL6Eb+QTP8JIFOKOSj<2o i{r_hMMyAUE0R{jed~S<h$DqLg0000<MNUMnLSTYUjZ97e literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconUnlocked.png b/Templates/BaseGame/game/tools/gui/images/iconUnlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..a471765ff1432092e7113e56b42f2798968b443f GIT binary patch literal 727 zcmV;|0x127P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!X-PyuR5;6Z zlFMroQ51*2J82WE8I8mWDN;4o2fA>rLKlJyH=-*k3W|aiVYC~e{STzB+=%NyKm^5w z3m4+bm8cXnzG}0Gw2c^Ol0K$0_i<c|k0ICthkH2O`|<modz6Tfj%u}f(pq~filSq{ z7Gul{W6ZrE2ww7^P-#G=Qn~K?{{5k$A?0}<04pmi%+1Y7tJS(OIXU^D7toh_d?=sK zPnSw1)o3(mHk$zWzE7!CQehZQm&@g!^?Lnr8sPmYy4~)pVzKDY&(G6rHZKN2pn@P! zi;IhwW@l$96bkvVv9UY73cOUEGsdv6u&@*aLG@2_YHF$iv^t%R8XO$VZ)gF?SZfgx z+k0tZVq%aDLWu~CS6AfO%u$R}SZg_R<P90oh&6~wQfvFvb=yyY^9nibiQ(Y2cPddJ z!uaK5C_GeV0L5u=3i>ty0#@^}dhsl97GUJu9lkxh`Okt_0|=(|h`!tol*)tahk*i! zgS8GLCq{|Gd7N1T=a4u`0gM4BU}M^~w|L$b3WtyL{rO$Q?PPfL6z$J7;&2In&vryC zqT@6`g9u_Zg@Xt1vO7RIY-D-g8eo{1!oIz%ujUZ52IBq#JOI%e5ertc^kIf}?KOA? zx9*6-2qdQc<rD3%jl`~Vh}J0uS|?N+Q`o<U%;q9^G35*6l(m8G54P<aBHQgx6gcSx z#NZsx#I$QQGMjzal!v}7I0M}_;nGj^>N5WDRs@&WP6I?Nxm{No*quRTbIBKa{opLb zA%3`w)e+)sGB0CNfH<Q5<aRO(V-TZ%r}=wgZL(O_Cbdfz_7}jmaaS&KPgno|002ov JPDHLkV1kHsN2LG& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconVisible.png b/Templates/BaseGame/game/tools/gui/images/iconVisible.png new file mode 100644 index 0000000000000000000000000000000000000000..92c8578664b8083724177cbb505cbf1800dfb676 GIT binary patch literal 735 zcmV<50wDc~P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!aY;l$RCwCN zlRan?aTvy*yCltB(@S%S4M`dcY1)QjRcy3Xs>P*<2zC*1(9uP372KpaIXStxRHSo3 z(4kf-D1{P9Db`Y&G`YLdCTa6=x%9s8yhOyo(Sk2L)64tgeg5zBCv4lszZ7wz0FL7x z!Bn4Fir8msGGLjo+cHfMq#Lg8UeObXJdMR-ul@oEg0N%BwWO`oLAwCc0dTwkUDe?C z(Fg~FP)!@UBRZBU#fL^C?OCEd%=7%AUK2uILI+pOV|%9vIuM2CjX*a5&9GqAa$v0z zQi%aDj33_Uh!P*0q=`bIkg=rlxYats%9mB()LH1kanwW^UzZmlwOUA}Z=xp@$K+5C znA|+<#y*mX1jxihj@aG)ZZ`Hd$%DFvt249skX4c2Z{pp;OROy|pzUz@;w_|aKS7_L z!t?u46f>Vu$-IX}h2TD}3Z&)@WBSG=csw37k0mq}1Jz0izu&i7E}+$tL3er3_I>)o zG<pIdtS`QXn~4k~>JPxOtd9BvI)3m5Mz7sPQ))n!B@9m7gGP8U8Y4SE)42*28ASis z1l*cfuF1B+l5DusOhzbd?24#_MsWGg9GqpKgF=!uas3Ph!!SB(9&k{_qpX{xdSh~V zcgc{dQ*<bXAj4qhJOh@l<6GW#05;CWbxe)17zoo)T1|-DmR-qydRub^9&~cBnO$>? z7Sdif`KF8QPq}@p6B@=MT)#XMMCaO24)+MDxUnS)JG1;TnXB*R{{(4uWf@Ybhybai zeckk9m)CcN@K6Izx64VStV)fHTH)t}O8ukZkD``a$HN!%C;S)w3*e^!0{}k+KPW9W ROJx86002ovPDHLkV1i|{P<a3V literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconbutton.png b/Templates/BaseGame/game/tools/gui/images/iconbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..b6f3bb6b77d03b66f39e0ecb57c9ec8f3a48a5c9 GIT binary patch literal 1013 zcmV<R0}A|!P)<h;3K|Lk000e1NJLTq000mG003wR1^@s6_=0fF0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#mq|oHRCwC#SUXP}K@gtv`-Nm4LHJQX z5lV{$h?OSd2SgE4$|w>k0yb&VMM?wwgEVna8vF;+_!rRflIREp5=HF7B*X?|Y~s7i z%;G!m-T7ks&Q)|PkMHjD%(t_1yE{7;WB~kFZyu0kL4yc_Y{1NL#9UyrIRp-YL*NiN z1pgI*FqF)6ba*g__e&_z`Nbz2M+f^`dPNSepzO{sEFM#4@*E!Q&y9~yEI*i>git8N zlpj5w@7f#T^?E&eQ7$i|9V}1`WLaJspO}DPFo@3FkA|*#QE<{DeX?UikR&M@2m}DH zOv#O+-1Cjc+xk(307Oy5;oK))FA6>D8;_GG3K=6HQ3$}q10FXJ@E(AP2Vm*}f|8Cv zG9i$7g7&?A4_cxGEgeD2grLO{kX_pY(V_>#8Ule<2t+lAM9^w!3GlfqK^xb+1yQmV zM6ojj$>I>Wx4_&PfB^WnAku4;Xly)zNeon+hM)#fQr+p>12(NSi%Cx)nGi^Zm8k}C zP!H4~%1RKstU9yR$fD4h%5tTCa}y2VUf1u2(zv|^vSW!wlBL~WmzTKP-lz)7F2#$2 zlOE}l9rNJr>e}(w?QN)5Dl9KN!>{irCx+fRoaKepOy(HYGWIi*@H+Dmo~Dv3c)x)% zon2ptFS(pSk;BhXwzAo5o-&h=%jH)5e*aD~nS_~{8F)B53#DSwKoCzP7=DT4KRY{v zQmOO?Nq0QdGa08+DHt0YyH}3KyShtwEEa>&(NQQA3L8p#pQfJ4*yr>09y2bw1bRbu z>iK|@s@LlsEUkMyodX83^7}zQjq;<d6H2H(bFCA!&k~jif_6Rd2mJO3TrRhBQXLw> zpliTDpv-B@1R-uWwnosN)0PQ>c0KU<{q_j3oj03J^+euJtJP}C?#$-FR<&9Ml75ZL z<ucu}qQvt#U&y~(c>$-Vrz|g|@oR5y&tS;mEH9+f={#O{>}RIuuRg%$`<HxeBx7wP zbg7v%IMz(;-#me8AD7$B5=JC40+EpsxpsYRAW(3unV?ww!OjXq$`(^1o`7&T(s#`? zH5KE^KHB5Cf=f~C))YJ(4r>YYCz4(#6m8+zrN>?#I0Rh?+-|qF%+)>r3wMGzgv{gw zaaaVlH<U)B0e&vU_444)-JNzGTwhh;4#9pl)h2dUtyVe&o1eb1AF7q#@biMDEOyn4 jf?r{0q)&F#{t7Sv0Lw=>FiH-900000NkvXXu0mjf;Oo!w literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/iconbuttonsmall.png b/Templates/BaseGame/game/tools/gui/images/iconbuttonsmall.png new file mode 100644 index 0000000000000000000000000000000000000000..25d5ae80b3f032c0e9e88c625a1332b5f2fb38ae GIT binary patch literal 1014 zcmV<S0}1?zP)<h;3K|Lk000e1NJLTq000mG003wR1^@s6_=0fF0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#m`OxIRCwC#SW8PAQ4~I-qh^u_!B{bm zg)Zt+Y^^O_6#M~Q2wj$fg)}}^-4(h~|3N`W9|jEAZGV9qw=oLVMa6~SqKO!zc^Ea? znM}{QX5zezlSe_yT)dgN-<<Q^d(S=hoO|VD0Q_sb@F2^a2H`l_fZc{8)&@-$o4_Wp z32XwJ;J+f^T9TPIHxeWGK91EhI`(vKGqJv)7gXUntZSoB#<nOk@rm{Lh}YXY@u05{ zoK7bb9(nZGv^T=f&(A$a>GTBZ!5G;<mgRA8Z!ZXffP=Z74L$W$!AXwfNsmoIk|a-8 zR~O*3GAF3MYv+1{SL8hmFPF>g1IOJYUNZtUP9q_`))0uI1fnQv2&ipV6r2c1uVMqS zAp+7<jMyN7bYBR>1_{LK2pU`kNxcdZeW-%~46DEzbr68gh{|xP9(N0Y8dnjNs|Km| zbc-vianh^o1u1t{EP5QcO|9;Q(!~Xk9*c>%Db|jUkKz2R2<3_ZJ@r+=Nsi=6kBuPo zF1+>a>leu7vMi*%z@MF+UxwaQI16dvsp%~UPhn7%<-1Si_G$g!W+v{<^hbC;9Gt}W zIjjRSv$OCy5-|v>@O`WcGcz+WS_Li=iA-XgUk(O?FgQ2}4~K>zl}s84{DA<&Pfku? zZ*LC{4-emlr>2%EU(Av6@bED7^z>W{`~9ZFCEVxpF}c;%)j7q81JpBtot>R`&l%$e z5jh26pt=f_R69C4>N1{^h}#rabx5;P1T{e;#eoU7DS~#Mw~u-Ehro*a)+BH^9L*6Z zKHUt#{sm?lLwK)Iip8Q5SfEW191Dd)0rL60W#x1_O|R@c&9AI{kA*@Z*xlV_A?+Fd z#N%;;q6%jrEgFr+5MvPuve|6R>-ENj>jW#iTrRV{ZHn{y!?n^jD`q%zq)aALm*rEb zl$L;^?7_i-rQ<#+Z*Tv+nZhOC2T2Uq$x0|+!n(M)Xy{dsFD)SwjkX{&y?p%v=HI_k z-;HFv8*wIgCJl~vCU!pHHWKU5*51w%hTGi@?(S~6czS9eP;k66;dFnq;|`HBBL@5d zaJk%;?@ayuKCK@(R2MEQet-_8B~Y#}O3}A#;k;Ro^?0I9P$6hXN75utUAq=;8?gns z(KceM2$~KkrBaFgYscyc{#;#Y{ou5ahbsiF1l3FIET7Na5X^u2!9L`3S;!o*yw9Hc ks^A6ejO0m=%3lEn0LAS`12sPW(f|Me07*qoM6N<$f?EmH-v9sr literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/inactive-overlay.png b/Templates/BaseGame/game/tools/gui/images/inactive-overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..feab83209cc442c5ad8dc73c2b86e0a8115c4743 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!foRZkbkkcwMLfBw9D?8~Obq{gPz9LVg>D`Q%4<v<{Fdsia^ bBOAk++056YCV$cfYGCkm^>bP0l+XkKL$W0) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/layers-btn_d.png b/Templates/BaseGame/game/tools/gui/images/layers-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6694b71eb3a475e8b540d21d964080389aae03ae GIT binary patch literal 523 zcmV+m0`&cfP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUztw}^dRCwB?lgUa0K@f(kIst=<;>kzw z?$LwGGZ;Y+q7UH3qi2r_f=}Rbh@ifM1|$a&k6yfsgt$y*(zA89)zved1rZ^gr8}wW zul`yFjWICJs@ku$-CLtA2<;oe(2IBa57F&*4W94M>*xVW8)#nC{~dK@=Vn-e(HfFm zfv{%^U@V)UD9}5SL{=z-0Il>eK$9_vQ@)Mlz%q@dbA>hqF1G?kHtRnGP!wItq_nd$ zSHfie9z$FqK<8={pdsj`#cda?Y!089+^<2FjieB#3c@g^{19@2RVv7U{i8>h4!ZbG z9l-Q#4OXdhcW3L!fL@$K5K4BiMY2U@sZ^}y<;Jg_Sl+(wD-dZ!08)T%21>~`wQeN+ zY!o{9cUIKF0Zas8FY$Sn1Ct~<v^$}@gf1;a+a(P-**pN#0KzDy+(qH!0AZBCd;8nB z@Zj{>cVlX%HZVv^Qe_o?cwWku%PzdN16LOUW)$_IvB|cjt*Ze*CgJ3`G0;DkYjtuS zI$x3RY3EZy>*<yKKIqKq|9To17WWFH+r-3Nf1qf+v<?x+eVl&;7y#Z^``Gh7*G2#U N002ovPDHLkV1iH6>AU~{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/layers-btn_h.png b/Templates/BaseGame/game/tools/gui/images/layers-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..ec27d03ce81f5b6d452ee4267e2b8fd7d5322b05 GIT binary patch literal 523 zcmV+m0`&cfP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUztw}^dRCwB?Q@u(9K@gtV92is-D<8q$ z(n9kNM$kgy1K3zvTPg@Xfj>nA<2z_TQixdE*o%btlicNgch}k3yUitt1f09Dvo|x} zeDf2mHCkF}4S?oF(Dd+0-w^F-q^#dx)Dd#=q|(s7ti!aj(P)j`J*^bPnFK)>lRz76 z0!87H08wOyLI_Y&jRPn$#!<r2NDfR>E7B`;NU*i#U}Q7?V*rw}hhbdU*_k_`Qy0e| zwh^E*ISHVkKTPsyn=DtY&d8&HG@U3RN+kGxMD-zL1Xiga0~{Pb+H%n5x9kAs7pqX) zyVZB+n@r0aMj7<|kR42uOjDUEHLHEK_3I?ocW*`vL=q4HF+jTm1!Ys(APC)V<UIJE zmipic9e_WKU7f|k#Bm1Qo^ONDmW61hq#z@k1HhyYeh^XZB6qR?eh|ZZ_uGx|@a)-5 zV{V~ZS|k}#XC?o5Uy6~319<E9ZC?m5rK}IlZMLoLT$cdS5Kd2;rTN)fz1d~xeFbi& zz0VLjPp|CvLAu+J;tw(@E35mt!5(5}X|zxzFP$U&6JS~q@K5;>U;v?y_|`ECk97b5 N002ovPDHLkV1l4G<=g-O literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/layers-btn_n.png b/Templates/BaseGame/game/tools/gui/images/layers-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0f253ed6e201be823dcc4c9f03ca6a83c353de GIT binary patch literal 368 zcmV-$0gwKPP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz5=lfsRCwB?QcDViFbqv~HZDA$ml0I( z9xenop2usbC@6kAzB1{wDuXZ&iZ-8@ye1+^61nd?0YV6pQeM8M4~XMf&<8Ih9|n4g zbF3m*mc^>T)fsGe9EZ4DO#tUS&sLi_9BdMqWSU^xHkzjCu>{0lBn-pwnBXD-`p0op zErT6jv{@9z?F2l}BTO!eBBkvG{pf&ob08`mfwx^8$#w@IFsm@ZogMdeUB3jlK!O>7 z1Q54vYuR`*=0j=;uIpOc2hyf#^cc&s)Jb>;O9B{i%mo5*UDx_u5SON@@`Yh&OUU#5 zvj4vCQ`dD`f8Y01Rh9DNUrRo379$7(%YnAdt<17a{3Lh^hyKJ{fB^uHN%h3O@IaUV O0000<MNUMnLSTYLXO$iR literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/leftRight.png b/Templates/BaseGame/game/tools/gui/images/leftRight.png new file mode 100644 index 0000000000000000000000000000000000000000..c29b7a9f8d05a32fb83de2af5b8d2cfe571a2daf GIT binary patch literal 3098 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/lock_d.png b/Templates/BaseGame/game/tools/gui/images/lock_d.png new file mode 100644 index 0000000000000000000000000000000000000000..b7177a257552c2082155675632be13c5feb19c05 GIT binary patch literal 405 zcmV;G0c!q<P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzH%UZ6RCwB?lfh1dKoEwfKq4kK;XpmF z(R%g~>O0uRt8dab@DcP4e1O*I#d4*IJ&Q-`!IZ**rDQuhE_E$PHaf|l9sbRHJF`GX z2m${J38++sWmydMfD%eFA2ij~0IK*?72ct4wda8epgQ?N>&R1(HxmGzygw>>W3|V0 zfc4AOyf=8yYGu0F(J#-o?I&F1oRN|q9!icCqHfBv3~K<!m>7<(Ti;qRdx0RBT_S)w za`a6!Axqbk=Q)SP;+67#{~WsABg#4FKY%qrx=tydK29KxW6J0A5QbMH1Hc9W=@+0X zu8N{40^j$c*aKKiBS?9kxAR@sg+up%e9m*T5zbCdVfnFy4SL7D-p+3wp(r>OeYfs7 zHYrI~kid$(EX$znI~W`OgZJ2NR0I4awgL<QydTt?o!sw+00000NkvXXu0mjfO8~G9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/lock_h.png b/Templates/BaseGame/game/tools/gui/images/lock_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b4eac3fcd7ef3e855c660a45b2fbcf102ec9bc42 GIT binary patch literal 445 zcmV;u0Yd(XP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzUr9tkRCwB?Qaet=KoorbNEpeHTsX*$ z3xq?R18@KiKq5+dD$YPf#Q`_~RVo@wfM_6Y6Nz*bF5^(eu?w$bo40GP@lR0%BaQa$ z^UmzNm4z(JfI<)iwu+)?;Bt*TgB<V3Nt&j!Vh>!ybX9{w{D4%EHZnk-7yBD-QZ-;3 z-avy}<QvlB-?3JaPsqonhEjw&XlxsA`r-|&X^IKbC=%AtG@$T0dVKztLHP!fB;DgO zL@o<fLEnImMU&knNm3@1d10F7a5TQ;w-_BMz?ptnjx$xOz0v?(Aq+OGjv@k!#V4x= zgH!1Bat$G*Wm+=`HUX<&UZ279@~4>3XE3@L?_sb-gCIBHVlHxB7j#{RxWhnfjzFnt zT6sWG6gX54Sf7w+zY(nC6Igw(V2iz@e!uMR93hSgihiTZvc%~7Yw%&sdSMvCuK(Z) n_JfUSmo0<8LWF<fM}PqUIdZZ_IN}q900000NkvXXu0mjfocFqQ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/lock_n.png b/Templates/BaseGame/game/tools/gui/images/lock_n.png new file mode 100644 index 0000000000000000000000000000000000000000..91ec2a44481b8c05fcd0ec45a3d812c35be527e5 GIT binary patch literal 433 zcmV;i0Z#sjP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQ%OWYRCwB?lD|%aP!z@w7ZNeC2?I4< zNNTjk(MNFf0c_LJ(a}e7bo3G2)zQ+CCN^<ol)AJO61f!c+}nF;`4d9)OTOI03E%IY zLr}6T113dL_(=#c@VG`_z*e8=X_}_4cm|o_xqW~L-=TH1gZ@EZisvT@X#$MJ&5!u< zmzao!?->kx4`l#>qFC6GVJ58E6jQVzCM>87U|QDn<o*>vzJjW%w|MjgpbTMa^w4aw z=Ojt;2{l<*men7Q#_|@UW(u$;?>*a|==;4KfK&*BHLs(H!hHV0>%rh0I-Mh4Q%Wn) z0w7oiyncOq0nhVzJ)1c&ycx9sY!DD^0WvH@uIqwnnh@6jNO=Wj!`OWSUDx4IKj3{z zqjoEtot(nrdjT8l9e2BVzj1{)rWo3du4yW_@2|jz74L;%2u=UN6||Fs(=IE3zly*= b^((*t`4X^UZ=ZsY00000NkvXXu0mjf7$3fW literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/arrow_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/arrow_d.png new file mode 100644 index 0000000000000000000000000000000000000000..78348024214fbb1be8d5e861de13a7bd2383c1b3 GIT binary patch literal 560 zcmV-00?+-4P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz(n&-?RCwBAoHlKm0RscWdLUL{pb}tY zWMp7vWd&ju26i^~2W;%@fh-_PIN8}0l$BK&1O){dn3$Lts0luN_@MCV^OyA?i@=uX z=;|`?^6@dSu&_|oqCbEBFdRO7SOM&OkWXk~2?&79WM^k*09ynKE<%>HW=<#DGG=CG z2BIV6+<~hMQ|cCxZ5h!P{bBgUuzU3phUL>X(%2%#Utqj?-d2Xwhc3|2qW{0a`0LlN z3@t?y8D2emO&g1TGyH_(&tE??6eZL#ynObG`WF2K`UV+4e)No?t7bASg6<avK5_I6 zLu+|Am4oizFRXaux*ZHF=B*)|aY^wJ2yk+8Fns_1gTdU=h++ETi8P3#f4><_E%X^W zCNzNA$Bv$4c=q@y^({)vif5R)WD0|=vo(X5q!2jR7S3KmeT%Y7GU06B08fVBe}6IT z+P#PR79oS+h(NGK_a8oF*uQ%paTYO>VUe_~41=YmF~jdaKN)7tm`$8T%w*O<!6AVR zKY#yV`1kJ*arF`#8{31oZ{H|@YC=*fzL3Z;1`BHo1{payyy~7mdj_H&Fs@j>+yK~k ySkJ-Ep}@}0%D~3X0k#m-z@QR%0K|a+0R{kM4aND5o2`TZ0000<MNUMnLSTX%%J`K4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/arrow_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/arrow_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2afa044699dac6ce7a99dc14a305c2c8be0adc72 GIT binary patch literal 699 zcmV;s0!00ZP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!T}ebiRCwC7md|U`U>L{W#whW$z`zEa zuA^lWJv4aPVZwIXLC{e=<W>bQo*Ww}h=+j}3r?9Hc9<vyk*cTOs`w8KcBmk%T`g4F z!YFK}CzJPAe4ngQ2g{rE&=)>5N#E!BKJW8B?~^1dm5PB7vIIIB&9DU}m2+;TGiiM? zl^%)8&sI}!{%Be%^0+=RaRD)kM<N8in~jF9FvbxjRLb4l^3A9@GC)J|I7g1_B0@<h zop#5(f9KJHY)%;98#ENk7~)Jk#BL~*6I9==&#$c(Oxe7X0N<dYh+;qlmPln_pPlWU z#Sg`@A)k93@C_O&6KZgiNQ7@F6;Ta-wRdmc=dWaEHqtXGCr&YV9zvkurw$xRbItHy z9QU{Ocy%pb`@W~e*(3TdbwUVJIfK*%^!v6^c=Bv9(ry4~D3mgU36^pc)p5G*uDNpW z!9tX+Z%>02QK-CdD5ED*ukFo0fBN#0)E0aT4HXU(Aw*gXLDx{!Qx2z0OP)_wUu|ua zga0uh5yc?R5fhOyDnHo}$ar$RnzM36xbCz%rZ`txtgb+zGf;HYf<jUgV9uK57n|$l z8@cQIXU?A9hdp}xx@bn(1q@ci{D@_&JY22@*VC7$w+Y6mv0KkZ*#)qmGYrGht)&`a zF0y|e8XrrajJS5OR`frUsf*4zQ4As}=<DY%vvF6XdZH|tP#k7tubSVw-L}6+acn_D z_3K^K`Y<DZJKvhSHdn}GGEN*@(12IDeQ<c7>tka>iA_zV94On!4~Bjp_Q+Pb=aqWB ho+TLR$H^Z71_17}OUQGB7GwYb002ovPDHLkV1iFXIo1FG literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/arrow_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/arrow_n.png new file mode 100644 index 0000000000000000000000000000000000000000..9f924edf1003a876e4fd5d7c6bd2c1229c2350a7 GIT binary patch literal 489 zcmV<F0T%v=P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzi%CR5RCwCFRl80CK@{C#P@s{h7%5C7 zJ|iJm_#ws@m4y^0w$>IJe?V&+N$jlr0F7TDjoO)LXJufKAnd%nSMNql5C>Se$;nQ3 zX78DE=FVMajBy4LA!k4_Fru){qP6wS^ZVjm-Ztse%X4>%l$1N^?DA3HtrJ2Z4(FHB zsEYNCNGavxWM*M;r5iFUA}NKBIN*AEc5b{=GCLw6n}K9EQ>oNgAFPwVAce>Z&pYkp zdRSJSYK85I$>gzZ4m%>PT0$y$Fq%kg*qm3`@*aIX8DlQ=#yC7L-BM{ErvM+62N#*1 z6d7$07eKO#W36;98jFq4_fZ0Gr_9#6{CmaSt(40TFn&baLY(JdN)y<BxvYq4)oS5K zoj%iWc>!BwR}D}n8w2Uy=)Z*Sa7A4rGaQLzNRc2S69f?^s(X#bE1VUcm!q}bqe2J; zQr;A}a}QO4eFXs8i0_A}5PC1~ChPnrd&@abDr^(qB%C&_gZ{%BAbF!=@HfnYQuLRV fPCF%jd<rlC`sn)WHnGXF00000NkvXXu0mjf=-ko) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e063a47cd19e338da32f1388621c1886c075eba9 GIT binary patch literal 1062 zcmV+>1ljwEP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#$Vo&&RCwCFR!wgcR}?+->2Vs5?Id=n zW0FY>rbG!!f+`_Z6<x4oldk$JShGM~R_u|&2JsiN2L!taAxa}90+G0q6Pr{?Y>&rI z0{)m8?tR9=1p&4tb4O2lV?Ez<Kh8aGq?MJG1puqGEDXRTNrIv%P*oMWre`%>TUHq` z7|^X$>KvlcC}dg2psZ9XT7{Cm%Af*pZhjsi(-aX6%9_n49zA?$iL^MwK?Aa}e@)XM zK)IxY25g%i2tdYnHjR?&E4@;ofEVlQNc?yPGpQ5|BiKEgxvKpDK70NgLBqgqE-zMc zVge}w4-O4=*V}#oFQn5rK0e0D$q8by7;?EB@^9Y2nwmuV!cX5EJGCFc<m4oFvso02 zMVOHY<|0v)?Gp0&JaV+s=hHY#XTAh3_XAk3*M-C19!P@qvLa(F7RTb!Wy;pV*7g>i zX~1<|c%IigXtm;e2Iw|j#Ky;I+TYQ>30acG{OP)mL^6qH!^PpjK5BH<_x=7dWn;ib zqagq}GtyBY|CEBNoNBD5sW4~`Batv1$06`0{ioSGXvho;`C>43BFmBhR8<AHW=#wY z1>w`<`K>atxR1x<s8*}uoqzwd#9b4K%M`x-0;p9nSJ}jsWDNJ}Kf_zOk2@O$R40E0 zf>HQ?T}3P&!&W9EvS&b-Rwt=?8X4#2lIWXZTyOpG=UrU8dIf*{K8iSHH%8;rt_~Bd z;*?>ABk(=H_e>2Eg0fx255uO&nAyCsxru*v^0+bj3vAoQ&h9P(0bP`KWMl**(Fh83 zjsxhcysSP<f41$d*Q7!@%uHMO+q{HxG?Vp4ogDip9v+}VY~RlOE5svAH&<t0+a>N^ z97;A`zI;X6DdF<c5=O_Ul%b+vv>*^UjE|oY@B90QFhe0&meuMf1?=_%%Gt3azU+-) z5Vcwj<(rFw&8n)SSU4b2$Pm*Tn4P_d2$h%_?|HoB|Mj&i&_p63qU14HTU$eDco-&? zj@K~lGsM*D(Git=8q*~FoGmYAuehseeifxh_D;{tU?O=IZ{NLzQ!e9#`gKGgdwY9u zNF1!GDJ(25h>Y8cCDEf3u~bDBGtKjR3fdXL*No5c7=%gS&2YGt1<4=xnD@-;`Ulx- zm0S}F)d}|TvqkByt?Za3&LwBeL}nxd_`Iinb%Kd<Qdz%r%c%4n+7)QqK|68=W+2z~ zPDPzH`D<XOpaDIQwad1}`r6CK*{4BtWB(Bc$g-5FzU)-0t52RhwlorQovtWFWk=E= gzRR+-mfs670J6WRJ^|UAQUCw|07*qoM6N<$f>Fxx3IG5A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_h.png new file mode 100644 index 0000000000000000000000000000000000000000..898ccfffb9792a0ba572acbf7bd902df63e21a24 GIT binary patch literal 1339 zcmV-B1;qM^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$-AP12RCwCFmd|e+MHI(pW@p!HZ|vBy z8(V<WRzeXRih2oA1S&`cdV>Q8dJAVRNSr_%5GT0vA5c+M{OCQ19>M{oaA*^iD&;_J zCsms^ao2Hd@2+=d_`Y=<wa7_*NmrVKlil}u?|r}XX86q;H!6&=i&%Mf{Ev@yMG~-F zF6T?-^05=Qx3=<ITU!^UHacG@7MW4;u|tV-|H1vdG=?#y6f@7Ew;GFhfEuQ?VvLL9 zhZ@a@CymxjE8vA5PnSpAmAkdtN4Z>f^<=fW;$||z*ida4@uVgwD%E2!o6TmWR;w*q zmgTovt@$;qY&LtnTAf*O-Bd6h7*Md88iVKqSZ_2IOQq6M7zFvK9~Uy|^sRQg{pRi4 zf6wP~lgp=1pITv@9~p;uBB_*&bi``z>?FYARB>tg#EDfY?MHFk&s$P%=BK8vOioT- z?Q}Y?tgWtndT(>%RUOA8;}C~<g8HoR5;=I(qLywnHWmwo!cwVJT<vsw77P(y`qQ70 zQfkW*+MAfDS1Ogu*<AM8&UW*{UZ-P4QN-i_$Hi~r38M$?9R+Of?d3N&?=9vF`K4lU z01U%WcoW&WibFV2JDhQ2OQ}+h6QjlJNGRp|trzIse!tJ3iU|(!gf=R1ls;t9GgX*c z?e%(A)Q<!tGmb6QUgH)Ef^a(71_%UmX~%JtZQJ!A=*@&tD8eujPsU0(;3bvDYBbPI zrF;Z+!zdB};9YGHR1si)w#+K!>DOaa6JZftAfZggTStXe6)Q%6S5L$v4)LVYR4Gb7 z!f^^C^1EN}ei27qCLh<>sTa$vrmF1H<)3E%THpNKJ9U;h=^Xp=!!uudUZ%F&YF(fq z0JHIaKjN0K4hA0aq~6!`D5q0^PtI4rNu^TCb?1`&ez^MESMzgcAH1<JcguC%JCl>S z9jexGY@cx!puN+Mv>(Q?;@swj?T<j>VGIto%wbd^gx~FU%bfFVGQ(cGQ)xbKe(-N& zlbv5U^AB1$+iLmm?C!RFB(?6Q+`6>wS`<dJmJkogOo%bep3FyM&?Izr$jHZszE;}h z5UkbOJr{<-xpX?c92ZVkyb14jD+vEUG8~${tdy=|SiFrEmMu%xNk@WWYoYi-QHiF* zF?jfpW(-C;32ok|-??1wdsI|+v!%Csy}+W(QHjPv)sYLSUPGdFIQbg67Z3v|m)1bn zWDJrH+8lkbMNxPTK^xHSXXw!Dbi4B7h0?dwFULt?&-Z7#-QIgh0ETyd;aqHx<MD59 zNsIO>wb%?#nt_qj;gure)hJ>ix>13)pCMoa`Zh2S9$<4mZnt;eMT`oD!*a&UT#uqY zhC*banFeFh2%a2YX2{q#k~tUxqn=DAeG`scLnSuaH-+y{K%bYO)f^mp(zc!D2`~L4 zB*QrnS{Qxg8beOEZK?MwG(*P0d1}nF?hV{v#ww|06aH%gdPhJbDL5+OrWXbr+9f9z zDl>4ewg+?B3_%CmZ=mVXl@7g5BMzsF9ynF;WX~zhF<Jx}A1x=t=wWrV14qTx2R8>j z)~&$^s!T|vAn6-`H6Ip%EoEr(5X>a|adh{RO3hf6j%YaMEKFsd?uTa2QYz7uE(Loy x!aUDA_KO(~`s5NqnqKjJ|033NyW#&QzyKU!!PB>K*z5oR002ovPDHLkV1l`Repvtj literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/bounds-center_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3bcbcf525ce4c61548420a2c8b3f2f83b358abad GIT binary patch literal 889 zcmV-<1BU#GP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#8%ab#RCwC#R$E9EQ5c>xv$NKi>-MBs zwj*n4A-P2lL0Z&HP#W5c4{4N`T{Os`Qqt%qM3$E%#O}I6(Mx9ZoRttgNsxk3Dl7D; zyKA`aWp?_GorP&in_0R%)Paw4=A3{2@BG&pqN*zQrw7mdh4G*Cw+_ofhnaH5cYAl; zJuXa-fi9r6ysXq~l<dR!*xXDO%3eJwBYX~w12Wyi+t<V4aO>gn15-vx4ZBxESOcOy zB^rI|^H1-^rMFo{arAb#`vf)Om&H^|2zR<eq0kW488uZ$-c;->EAsjMrFbs7<L()| z)z!V{XXLzDLd@3L+2H4)q2WBN!+=s#b@V03cLoB1I-cj7u_rR!#55@gWj!HHK3Ld~ zIC3Y?G$i6V3WR`Qb>+cc%=V_X_Ki%I#O+>2D6(u<D9OZxhjTbqu45uJ8zYn$c#)K} zBu^u6jud1xOYMNbl*Fu3GBa0k8`it3m^Cpy^t@LT#myT1#8jPX204DJ)*lQ8IiJ_d zr7ch6A`yjq@bFRLj>6qOhePHhN#a_sTqfjeIGUODJcANfM}k6IM_Wt6iu82ibUNp5 z?{ek7EiNvum1W0ToC_N7cw26C<jiNO5z_JY&YQV0i?g#<yArI{!3mFtLyh)XES6oV zDJjD!mr*2M3s3~P{=`J7K<9dMq==D?E>MH@m<E9-$#y%3GHC<~VBOL{V73+w!vde4 z!})$e5c21Xt0DJ7!B$xE4mDU7Rn_evxn#4=-NVX6l<LZgXRK++ka;yZIaS9JP|))j zN+f~j>#3rVk@qD{jTdQ;U4$4Wvuq7_-n*X<H(#Tfjs{VJdVS4w3oMC<S<)`J%ufh$ zt;x>%1Q!n|iZXzB4r1>k`tDVlQ^0w6pq-VLm1VEOx>od?QZV?Hp_h|RH=K2*rY_5o zq{W+HSq`qh1#e%NnVD%jcBpci$;AwbFeounv!6B^lLTf3VOB&LR85wWn9CVq^hcjI zqs>8OEiSAnc%obD$<(<19CW0r=H^6a9jCKA{@)c7moPQRKl8KwMSuYSKPN#kr8aJv P00000NkvXXu0mjfx+|lz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4d1d130b3b757bb2b031b7f12319e16d1c57dfff GIT binary patch literal 934 zcmV;X16lluP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#NJ&INRCwCFmfK5IQ543%IhUCs!K|n( z8qayr#&S%%SSCU~7(|&FMN$0?+WjGjqKIDVwO$KKqZhF-?~g{5mXl&*1<piS;H6}S z=FFK}Yi%3Rhp8EL+_2cN_h$cCYp-wZWqJJMX$gR4G94Khl0||fOHdUBs%rJBs#2#g zP*qf2(;Udj$q|P#HT-_R{%d-=nV|ycEGR%$)(Xh7oH1xL8pW&EeY$XqJ<K$a2j5TO zg_MYUrh$n;PYDUjJQPJ)eyAi#P*OcSHT4tETiY-+I0&E5J8w_ZbQHN<s5y85t5>i3 zzqhGEuU)?Zo6Ux@J-e|!Hy34PyHK`s2fltA!^cmb(A)bGXS=&__wF66AuxNC)Z0{m z?VUXc27|bI^$N7SJe)jn94##^xOwX~`ug8O*EL+ebRKKht;4hDtq6roA&*5tM@L8D zAea#}pyxS6b-8@;0xByjh1|Njdc>ZlX$S-YFhU`iW*D(pY~j#~igJ9W5Dt4r5HyTg zb8gNT=5iHbVrmK_9uI7G8^Yl*c^W}H9$)yI78h-$kQumt?;f1aLX>VVMWNFv_Po9? z7#bRadtgAEsjWE#qLG(mG|P)gBp&q*4e0FXM0a;Lni?C$T6XqII2;b_tExgrd)v%G zi-i)2jAkqrx^Y7;s`po;XiFhReLf0p0fvV?$fI{2{@|ITMFLWVvm}A0RUG&dHvX!( zbSknUyh^OkI|oZr*`x*KY!nuaAxg6uHW8;e{&zj)s+8{6#FvLmlO$nM;)Fz|nj%}H znSq8Q5dkqsE({HlHW|q9NF;S=%86vR+u`;4#5);ANPvD*m0~y-gYQ4SFFo{d?GZeD z@BsPw1tP?pEMclT-^Rw^cDwO@z>V^~6-x^}cC;3yCB=C3xCt*>TQNR9KC9DBj!igq z<}^yS70+gFGP%tBscCc!tj7!I>O~?&=?%H!Bubr-(qB2LstQ?2elIaouMaZ4r;Xm0 zh0h7a(uu`m^IA>r47eqc|M9gcR;!xUug}I8a$$(i@ieh7%m44U6mEd&JJMNasWxYP zxz(C}r!z4*DgFrdw9X$0G{5P8r;E@~i>Sy7GBmi)srw_q0DIk3l3&uj4*&oF07*qo IM6N<$f>&##w*UYD literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..90709bd63926468cd660690b67820b40c95159c1 GIT binary patch literal 1101 zcmV-T1hV^yP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#?@2^KRCwCFmdkGwR}_cOJRG|%at22w z#;s$gQiy}xmbh)ys)_&=x~sA%Rjq==e*hA+EO@9$RR!xV=$0-DltQGai#8D0(QaB~ zSCMgG(Ks|Fq2MHtc*dTYdpYNh$Fk!1(S>uRqp@bj=ey^39&@G4y_p~YB+)eJ{8~o4 zqH^HL-fgdHT6cHni7PAG;^Y64D(77D`~AQ;?>Lk=3yY66l`{^^X0Ta{coZox#z2y! z&I6G}rZUceOoGjU5=+R-G16uk%`Ytau3fvnE0@a+E)@#V+Gkq>f!RnTGIQX-r>Rh| z*Z3duHZP*{=guDv1bTuW?b`=)xj7gac@IY3eFy%X&%^x(52APO{2o2~_1WF0PM`Yj z_18jrGu4PE+ZZL8EAx8$@$yeGSyqB)&YXluUmqkt{|qK3Cg7WI&%vL6-Gyj00w=yY z2HQg+RAW4`wpLV3%ak_q<+5awmN8pY@J5!;&j-T@Gjs!@ePQsZDxCQ8IE;>tf~IM3 z^X4t_3?&9#*I!;)dFIh|L$>V=d?JT<Dyz!7vBZf)B0GM4e0L^01N|74yJCX_;@Z{a zfhUCm%w)3==;<lzhOT%#9$QsarYK6CQzojHBE?-bYMBo|cz@yYrAzSrg$odi4Z(0? z7=~go@oX0JJ3T!O=~N0zB?Ar~I&_yJ!YCz*R9@%PZBLa~bZ*tBM~)ta8<RI+YHA8- z6V8O`hHy9xd-m>y$=`mh_91tc@LSiX1QkWZT2G|V_8{#4ct6B<4#7XU9Ll=`GMOy& zVV*PhvW|3)A1=$1$QMPse%WrM?LbKhGFg#PHW^e!rJL3JE|lUF?byI)wUC6oEugHj zIgvBGmd&y=RBATu_Pk^DP+nU|ttOI?g<1gLE0xPoMk|#{V45bvO>kutzC;jqL-j-< zu}UGeTBc<Qh+!B4ioGa+1h*`^I^t^8XbR;mpxAT9>}<{_%Q8Ys0#wv>0XOjXqlHJ6 zBC#Dscw?VV%d|0j<8Qt-`NPGFV*>*_s-Ho#Y{Gm#59xFo{!XQ_iX(s24)sPpxtmkk zVY4+h_DL!h8=Sj+>-L_XuU_pf7K_{HzPHQ!%-`*Q`1bIwOgtWc==kK7IcqnUx1E#2 z8~y!D!GmA?f;m+!%aSn?31_J&di)@W-lVaIv)RU$VvT%O<fT$>eLj-V=}SJJg?Z(e zO9lT-qfS5xM)zsvdc9qY-fiTQcnD=82kk6!?DKjp8s^~iceTA8Fh+|}6OZ#oP}W{f z>){e==S-*iF=w|NG9>Tv>ho3Yl`UI3enwhbT_u5*sb#J_Ei5I`HtmG}LVy7PO56EA ToqYNc00000NkvXXu0mjf3grtS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_i.png b/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_i.png new file mode 100644 index 0000000000000000000000000000000000000000..49af5bf832a23f0031eb219f575129a486d1d9e9 GIT binary patch literal 708 zcmV;#0z3VQP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!W=TXrRCwC#mP>0BQ547jGf683QUkiM zfy_+7#6;XQ)k!x2yQtql^ix>y(S^{Jh+m^_>N+cJ8<!QUH6$g(q$xD1HdWAC@R50B zW;}D9ln{_fnu$X3z@K~P+{bV3xo1woS(b&rZ9M*g{fC}BsNZeE1!)4w)U=PfXGtMa zuxrXU(jn<fZvB<fy?(p4(?8U)m>`G&c{+vN`YvSoD u_^{W&R<#;jTYDL-ZdJlb zIaSQPUT+N?UOHuHZ0t;6Vc`KpLBOq>H;~C>@bJ-NY`o1ul0?kkzk`S%_`q@JXbI=V zod#$_C`|>D5W$GwkNJCdkxr)(3Wcz``hty#qS%fc?*{HAyocM>>UgPK!Gt6pIpY_j ztQ{R4!RLbmRLW)h(Qtsa{#fcbw=(D?(i8*56PQd+A`y?Xu^QcPu~<YQpNFa{phVhq zmYI&MK@thK=jO1oyn<{ti>FIV%tv>PNF;)5Gc#Cz{;WOK>d>*n%Pc&89<$eHarsgL zJM}t+H-=KF41w;s^sYRd;=pkn6Yt}}b3FV$zwJEocbVl0)HKcCGE5lMYPtrCTFdbd z<)nsTu+3FXgGN$S6}qkyTxVlLf$FUmgD8r^piTNVZJ-TrwG3}-Z~LINTHVL<JV8|k z`q^wUxI+E?kNZP`zJIc~C`F?&7GgVbbWO)zqk%%9fX#ftp7W{ar0k_Quo^6<P7y6k z-P4Y2(?~x|!}yxtc=Np@bB%Te4^$lJ*TV+XU23c4R3cBp+xhL89Z;@o%(WWz3+>#L qb9pg%3GHk1V<*yy`D6A=fB^vV_zfW03MmBu0000<MNUMnLSTa73{gG+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/delete-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b5c09a25d5e4b2d2e923c1903dbf28ffe42b2939 GIT binary patch literal 617 zcmV-v0+#)WP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!3rR#lRCwC#mOD=rQ547jGYph4rI^fv zVqw-$EIt;SO`yP58wEQ*fdo_v6GDtJehileqLo`IuqHH6`Pd<wWtK5<m&jsa2$eg} z9nZb46joErs3Cijf6mOgC+C;kbMKiEqA0@OHWB~8deHL+mEt7bnY}BMyGOc7n)+jT zn^YndU%gyEjZ;sEb7ctnNmaF6{`w>in}^8fuOWZ+3cen-aA4cm+1Z7CP-noAIAAdb z@YdkqP;PnoF*H@h-0W?XN+mpf_8jj&e1xuRSbDSwMOA<AMj}1UQ9(Fu2fC`DUy`u& z@Bs>i0%Tdn*48_|rfC|>m6Ph}>(92mQnTt9(Y3RnF_z(eV4xr0Pfk#`EcTjcPkEAI zWHE7h0$Ic0@dov`TCKvYRN%S}sGmY&r<d9N`FU(^Y@%E)V|{In$Eep(6a`b$)7W_X zCfO^)?BehR%-op4_@ykqG@G=&qo~y^sMP1$zLm~!5Cnls>=PjhBBVZv1<&HCKs%kz zFNGb1<UQX*L_X@qQ-OwI$cww42an{sE_~l7xX;&8f%-wfA&%p4Xq)aP12T9Jqys%| zG@4XAAgIehKiX{$cj)=}+i~&(pKM{ZSbUMmjPfmJ2gmn(9JN|7O%r<+ljS^#eJJDp zBhi4+$*r)l*cG1--LK3l4TkN+p}jwc9`wJ1{uE#Un``ZeM;acU00000NkvXXu0mjf D#T6h@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_d.png new file mode 100644 index 0000000000000000000000000000000000000000..8e27bfefdde5dab2e3a6beeebbb8c3784b71b82c GIT binary patch literal 1559 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 JPDHLkV1icw;W+>R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2ff2bf46a4bd0d754f561806466e7760cc4b5181 GIT binary patch literal 1752 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_i.png b/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_i.png new file mode 100644 index 0000000000000000000000000000000000000000..54e78d5c184486f24ba9f7b83f902dbbee5ed0f2 GIT binary patch literal 1195 zcmV;c1XTNpP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$O-V#SRCwCtR$XWuRTMt+xBowBc9QO< z+O)+YY79z!7cD+0D#egmYOA$kEmA~6@drUDKKLR&DTq}N1o1&dv{GM0MT?;Hp^z;m zCQVW`iOFuVzcV{KGxvJV-E5ql>~2yJ``|2>JA3ar=R4=zbG|zg=N$g)M~3V0{C_~z ztq9$;Yxgx7otmBLx-T^g$!mCc-(4X^mi1UX9u0@Xu}CBeT0kq+%-Z6YpM6pxJ&%oM zCGxk=z85#mTFSO<?z-*@W3KV)OE0<|K}Sa)y#psj3j`pMNJ1nM5$lSg02%;UmO+vw zxH2_8e(B;b+ylrDA3Gk=G<9z>nS_CX9T1I10D*BhYqJe!UU~WAhV;sclGSy6uc~Qb zS!x`$sx`J1{5cL9sM*lh-%m1gfnZRnTbARHWzGkf%OIc6LvA?-j_ZJ`sF3XGfjxV6 zyNt6rPj<LJz0$~`!-I^m6h$W_aGyNooI$N-fo0WTVSYaM^fM>b*IzrcI(+9oC7ns{ z)HDsPN*Fp9I;3wJrhER2&&NHPWn|J{Nc_`}Ke~GV!v|969dO2gF^<C(ky@4o<x&YA zc<`arp@SpB^RLdGOVB!j&{1%bM_o#MsZ^ZuM%b-oSiNq|S|X}C;5&{wMp+YpJ8rlU zMjkmJ4BdOr-HFUVMlP(b0;X9wMe-KP8irB1@ZGnT2aq*uplzRi^Y!_>VN|Q8X@F^( zP$`$m2o#G2IC}I5>>AuD<of$``QYd%t{1^V#|YFgOri7p)wx+8U=y@+pJJpl==Bg5 zjqse#Ns=UZ>WRm}MgZ!?{rm5OP$&f0R$x@BLeIscz?#TSWXGAe!nZ^Y*cMizr)MD& zjb-Tn;-7zr^;>QqhGvmb8*S_DB@m#jtevepc{w}p1LRv~q*9?UkE0h~gGd;VPNzj- zH8ZEGs)&-nRRmvJT!fkFY3JtKhC-_=d24B5f%ySj$YJdC3qhPT95I>|S&5^u0Je)l z``C~Ikbb%4WvEsvfQ$vY-Urk!v{tF&S~MnPT0lUm{`AB5zix<BQ?(&Fvu1CKcO1b` zP`ul91?qNP^dhm&x<TAqTKY>wy?uMSQ_`Z|*6}csd>IvQZ2|Q>N?0NOMR2<ThLq?a z8Byh?bR94=9ghiHZb}f_S|ztI5^|`H4L(Th)An{idL*Kt{-wepx(s6VuBE3XLk)R= zm3)2zThQ__65DXg96NE6UV6d%8AICiA7?6@VxdsB9XlQj27x%Yt%I0}-zF!sKVSIK z_4jMLt4(_L`LQ9K1OISuy@b)_KYage*=zW*Cy&SQNYa>rh|fv)gLmJ_`mVoqMrwPX zlG>@U)7##9^Nn(wAliP(ojCPeKSrKGdlu?Vv;kyYFY)zCZ*a5!!scy&CEsYdRlI<k zkwa71e@pNVvfV(h=^Kf6N@o{YX|tnH;opBYbyB(>U-*9uFaVLXQwHSr87Tk&002ov JPDHLkV1k8eHw^#) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/explode-prefab_n.png new file mode 100644 index 0000000000000000000000000000000000000000..09a7ddb2211f36b0a2f910318a8a9d011bf275fa GIT binary patch literal 1300 zcmV+v1?&2WP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$wn;=mRCwCtR$XjVMHD{wuiNgn-3r^X zZ50b@3_+Wa8WoWs5+5{z*jh@e6@-YyG+3L87!wm>j4zEbQ4%AXm>9GPNDN}|#Z-}s zf%HKmMk}?YDy?moZnxX}zjr)mcCOxCwzRz9gZE}KbLXBp-<-d1Mq-S?wSHvy51#)I zsJalLo9mVZKcztLCqzzSoF5*#qCUw_q@d#ZhC6OjWVyAptZY?DNy**ea0Ij>t=BZn z1D_o|au!7t;Uy1S#_tatd~c0mn9Yvkc$rM*sOM#VcxB&<sX0NLn_GUyu3RfBg7Wex zgu`JzuP6$j0+3}HBw2#r&JV8szW=N(KwWHB>-HV>nx^(dqfw}-sf9=+3<!+P6}<!d zUw*0PGWW`ga$hVKJEUsb3R%jATGg_@6?|P66_^fGR#o+&%nL=KP?>F66RzV-0b_f; zj0cHC0wyOW!Ogg!Dk?-PDq#8YWhsw&U8tr=gdvmfUZrVkYq#flEo2=J7XGK5GUkD4 zT3}fwj9$F>@zc-jUi|v2`;XPHYKX?G<F9F&hC!B4E8}Zuy`dZF6URPXEx;%Q4tXX& zk=Q3aAMJZ^)8-c39Wc)W&tup!+)~T3U?!D<haTSe*w!sg8p(ft{P<d$ClCq+BY9j) zFsamZoA4%q+5(v6mki&HZCf1{x5@?_W3$mplLOXXe*-i%ZYDsufBm|9t81#m$*Czo zH}g%AyoIv5uAk}qs&_a5X!yW|>eT!4ixY=+eYW2)bTAA9(laxp1g58x(AM?@)ZMt0 z3szOeB3qi9aef*s+!%rCy201{K62rCp^xHZ3D?q?%~huiEp5kLpT?x&JEu5Fk_0=q zJxQ+MwYXu!15jLC3|Lm6r)RSW7z)gZL%*E+Q5RM^BFnvb$pOn^q`acy&2Xf&gTBZ9 z{DaT0xV;|yB%?CAZqXtF0rHB7ZF=CRbKOFXBdl~6%1A7gOm<^y#PL!j42Z|$Jh4VD z3}+XsDz}orRRkX&8-t<2!O2@!-ge8>rNq$q=;*BP(t_mB*|oQh!i4QEY?X?v+=I$$ zv0S3`-*p`hNblst1k9$>fQ$vPSS3)o&|G>J=OU$CCYLv*{?p%lz40<jHC6kTV&>N- zA7cv@hxpjB6|fzf*CMgbKML{R`1oJk>Lp9!b9t?SAbfHNFp|6;7xw0W1|FqEA^t^h zCkuv@=pxNg<}QxK06jBjoUr7!VOJ_*Z00Z$a%d|ScsH?6!P5chk%)romlB8QGKASH z+>_&>tUSP_MB))F!4YAlN%fjfgYx!WyWhh8Q3zgRXf=IfPl+>~O!hghvnCV@0da1@ z4WcK09T-@9rtehRH&}pLg^o7m*^bWdu&@1>d+n2G-Qf@4KX@vr`0=N9+=(ICgC1C! zx5?B8@4mAxaMn5k&@M#y{^6{)cXn-h`;G3CdAAFnxIHmuLSEU`zGo*|{v7HX!gY-S zc{>2;&3T9mMU-vDfedG&=3LQOaFc=NAMi{NgDEVXmyvvzuz;mrz*3f1@NNK16JtMd zawu?KM#`hFD_xL%mrG)e9PkXg+KbuM-o$;N%z`dv*Yf#WfB^t?Mt-XIz(FDa0000< KMNUMnLSTZzH*?tl literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_d.png new file mode 100644 index 0000000000000000000000000000000000000000..75159d2661dc57615ec97b4b9aec6b22c4a04382 GIT binary patch literal 977 zcmV;?11|iDP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#b4f%&RCwCFR$oX{VHE%E{@VsR8-kz_ z42*hMl7b>6nt_Fo6?-bNhoS=OMXT3*>#2tbq9GCHLsImzo+41P7`;?rAbTuEk`lU2 zow<Ma-u0bN@7&$ib|<VOm(P3mo$q)4{LVR_&F}ZO0|>CkgBpm<W`ih-kR%C;tb}Dn z_DKZjv@4$0*28eS-4Fx;wUTHw;#rHv0tCtdkF~X--sR#J)k;)V#oX+yhr2}{)*6t6 z&X;8w9F#&@Yd}L3yBz}lv&hW3vhI!cPMY4Y-ycNh$<idBc^E`65Hfw642~AFnW9?B zvL_#IFal`Mb)CPbQfZb7k(WWfjNIwP@OTe2wohaG$4@^;B9Yupoq4}hnQ<BeN<q)+ z5NrZF#73Fn^;cM4*=!bSMun!SI|imxTnDgM2TC3ipd^yDQqJ;H^-P9Hvcy+@IMY$J zy(n1%r5tj0GiwRZ>2#DekPM-R$kOZcavfdI7MD)BLx?n#Lkj7+zDs*~o_>I7{|iJS zF*G*1apl%!9Q7V4%5gH8;<|?CmcvV-q#TkQI@p4xU(2|Aa{|dk3J2US{P@0v#l;CU zH#dRKe){ThPFG~QlxI;gldfW&5&>j+$F}oBgJ@`|=Va;Y@5boZRld@DG_uYOAn(%Y z49}y;Lait=U#kK&az%2YzXux|aWuCy;ljls44gg1S9+)KB#A_Vvz=H^dStY)AJE)p zQGml?M?C%qtH0OyH(Had9vu#4w@lK=WHLpJttzLc^3im3bsa}A_!%=(uQB&_9+9;e zR##)_X!l~_TZre7|7ElL1O56jY-)QW!`E{EFa2EMk0AtTdD@iw@!7q1=w$tqLN)j0 z4R34PiEU1l&_W1Xn_H-J)xq20Ez-SxX1ZkWwG>{Bbrd3n(S?W@3QJ!{cY7{`v>2sm zTu%&Lo&6}}eY4zqGnq86RRVT8oF<Y`i_8(tCYadHb=3^(P;zykV*^uVN1I{5%SWGz zMjP+jjC{IK(0fJk-&I37p*+&GFdBa0XfwPj9BmYOnl|~7$m?=a<tEO;wlgiVmPZ@y z-5ZDIeG<EeTIjv@To|p6HiKlwe$$zD#U75W$2@dBEOj{(#o}9nmC;s8R+g7(2{TU> z-^OM<FhBp^Bg+cAh0D8B!B&fVS(rV(zXA*ZzY}_~{4tNO00000NkvXXu0mjf7-i0N literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_h.png new file mode 100644 index 0000000000000000000000000000000000000000..a2c39cff4f84d07f8faa494767e9b7f15caee290 GIT binary patch literal 1253 zcmV<B1RDE^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$he<?1RCwCFR$XWuRTRE|Gdr7YWVKDV zg-T<wwb^2WA+1u><RMnU2o-z?qDWB`d?<=9K@x4$S0D7vCmZNP5V1ZK(YFK@M1>T^ zyjbcY!tP>ice8)9J9F>#oI5+~%<d#UZ7&>V@9aJ2J3rq!cj&dv%>#sx1&}Nm{;Ghi z2?WgLa@l>k+|Utkmdn|4xx65hR@uAv?j>6KLkC5iVyT!FS`#9rB>E2IR)HWMfNG+Y zB!sfzgK9+(PiUoxk^s+C?7+ttKYus6KQni7b^6g`g(PENUjBae&+EmR=(`ISpIaFK zRhl54PzVZ<Dm7^A)x~Gt^QBLfR2291MuQ11v=BU2jroO#*CB#+UrOqEKD+ec*Gt>m zHO@HG{pS%+05*|Hh_E`H4sErX0%}e<=bBQc9mkQB+L}-rJ1c!j;XFez4%TY|b<*h( zpR!aB6!C&cfk-p~)b~9w<WbAA6bc$)jB1_WB!y}T&Xl%<*)zr!=rYUls4Yboup24n zS^7RW9)~?WGqDQZ1HY)|jH#4u%U)DcCT0d~Xxp;nq5Jl4ppMTdMH}JyfV754L3;Zm z%}*UFpr>$7zx~!bOX2%hUVr(ct5>ehG@GsL$Vj>}|Lpw6ME+<csylaXa@}>?9CZ<> z2a1BAN=w5io$R{t9Dg1>a1a}teEP|y*T9G~de<l^70b9kp4pQr;hE+47gs}729ZL# z%~M6nfp`p&eW^&m;<zv<6fn<dJaKl8>=_-!pU&w=PLUVSzd-PbdtmqYc6EEa(QI+I z-DW0pP!?dW!e?j?aS#s<V5C9blSFk>Gc`R$>a`jf+doF0eEKYTZ1#+K;=W$5n={pF z)vDJUqTO~`pa-0b*fKaE0g=!%y8ASNG!Z+MB8^6a{B!e`d56z-yG?A{#`YqOW>Y|~ zO3M-wm_A?wM_@oC9kk5<p<;R_Cm$p~{rEHa;<GE{+x73r?OV5v<nwv5wY3E;=2J_E zZe$6)Z<wV5_98mzqtf-?%H#K+$o0nF8yDW}{Z}fMjD!mTjm;=0Wb(iX*xMb0{p*(> z{&?xg#J&}DW%+}}6>Nc8y=J#suDJ1gVa#p0j+1uWBS**oj%EWR`RekurE}*`t?ZOB zyib9~$rqak>4G%D2xK_+G~--B{rux6juoN*XxMu+G92(ssEa*{LkA~W^ri`jw8Mn) zJ)c4ga3__LIJ`%kv?p!5SN!lIFhoPgV8N8vJ>5=+JHaH=(VM1wpC;%3o3&q;dlPE< z-qpho+*1JK|JBC|o|_wm+45i2nSm^UcrdJEI^lh)6Vm(C^CwLm-L@;R$%PPRfWy+f z8&$X-di=1t+Q510`H~?^@wUNh5j11=gZp%sNU=tgQ0;jh!|M(ADM&RWQTJaE2#8KW z+c2y*?$JTiiI2hX%V7slbPS1u2D$@62N}`>*}+$kM#VLc4wPWVN9>zO^{$Z;lS<Cn z0PPc}7P1$nQBs@4YKkAgFgvrEOlIgGW;j9-4{N$ssZ<s~?${UpzXA*ZW*R2UN*YQ7 P00000NkvXXu0mjf4@6nw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/fit-selection_n.png new file mode 100644 index 0000000000000000000000000000000000000000..5b159dd02c9cb3e345ae802daec2d1cca56bba4b GIT binary patch literal 694 zcmV;n0!jUeP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!SV=@dRCwC#R>4l&KoFhvx^^Up%LxQr zK<cHm6tyaFBcKG%oXd~&)Rdn35mnVsfNMonir~~1#DQ=@A}2UOz)29M$-1-AthCzN z@*1fiRb6Q`S$lS7Jp1NNSRBW2*UN!>SpS3m)#awW`0(+QiLizD?^ZXHY+t{rzC~5l zpP?<&_eb^P?4WdzqvBdc9-wqlgkdLxu96+cBqG#4+5KVfW>i83ERZuna)T0+=!=m* z<+DKc*Lj0FBpni!c~W!7WTW17$iHKKr}7R-3vv);Bp6ddT`F}a4X7-QksghWt2^}O z%9WP(YMekbhEkUSw!UoC6I^(7{+GNGYamh9@(y8mcmz&QPvp-%lz||42w`|B_sf{n zzWh=QzF?eXq;vJDDlL>NWhe##aLxy&XJ%n#<rPRR_x;6U2~Fnd>s$&Z=qtr-$e~yN z=Tpz18HO-8I0TEB!1T)(JuUaGW>eYr5o$S|6G@=6XC%hJ_Y2T&x1rH!^d3naMG<(O z4_cdeS$<vXpa{69lauiC$9Gs;TZiqP9SnK_=jUhecwzz$Yc<uYO49RBLod?cyrMk> ziq+6XAdOOMwagZ?MekRYH9BKNYyDD}DoalshXq2!;kS*?2MJt!ZE|m3M&cTO_K=K$ zcC`{R4J?Y4)LEthDi`;9)T=V^l>rjRXfIuJjU4QQVwr9-MjIo$syW(>D&?X<pW47A ze31l7Es#EO<dli>(4ZP;t~TCmMw?@d!(2e>Ql^fUgw1GUhJCJ}BBhjBqYZ9F4*%T0 c!QTQ50Q24FoRy<nh5!Hn07*qoM6N<$f>4`3`2YX_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-center_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-center_d.png new file mode 100644 index 0000000000000000000000000000000000000000..284165852efe6cb9e0a9ba6b037a8a85adec0c12 GIT binary patch literal 1119 zcmV-l1fctgP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$0ZBwbRCwCFmhVqfR~W~??fuoRfl+i0 z!!&iJwnY&(<_|Q}37fyBCYWXO-CpeP@NeK9duhxPL(s^(Wm0Bwr7%d{m|Mgk86_xX z8@8ZCXnT9R=Q-C_ni6{5QlFcfo1Wf#KIi%MJ;&DD+uH_UfNl?}U^bf#k|aTvWvGf8 zS5&22W}s73Jzj4U+-^7Qb~~zN$>dYdQZhBbPyyWE(t?`WTCt*PnXc;?3WYqvE%vb5 zKsKIVQ4|O$hqT(jf~lGYJKnG<<lO&q@9)I~hVTA_+4vkX*-UZV@AKi46UVWmVP|<Y zb5#E;aBg-Mef>A64P<<LtOM@)ZDM?7Ifb$D30%GQoftoI^jKL(Yzduu@L<#N_4PFj zhJM6bPA5KU{}3&$t<W3}5jr*o_4oB55*@{^UAxiH*a(Th1^AZGo40Ol=`DwJC>+M# zV9*@c0RHmwW%Ts)U?dvFg^L%(qAWq#PJQ|r-fwE6Qa8(8Rn_9eOM86y_M5NC^EC4L zd>Odo@F9fmgptW)VJB6RtU_}}4%uwBWGrvw39jIoVlzF?yecBWcADU~>)b_A;5lST zwg}bBp=^()A0k?7t~{O2l>MKlek_<lE8%$z!+0G}^|JBd;k$60JC8%{?F3D4`t4#O zK^`k2^x}G%f{IWhXB0hko#@0<hYe$UwxKcL$MJzEuImOS2fxRewLG@JFz`G)B;I|o zx{8Td47=ZJEZK1^LG>KvLpPzzLxXToK_zeWLu+V2h;plW0$BfQC(<W7@kG_2Qc<m> zl88h`M1GHS92GFW3RY1DbL6Qg1Ol_iv59eN6&<Z@ZAeT_qpPzMYZTt8KYqvT%p;7C zj^Kyex5fKs&zy$S<$|5}R9Uj#GO>HHva#ks0Ir%^bSxxMLlyeLfdlyU{(buAG2Y$& z4qEBA-ybLfMXM<)Z7Nb_=ognRk(XJ~XF65iZ<CWqlFWIVe4@C#s|I<U6^b-HGlTlN zI-EZDxfwzZDS5`NqiU*Xv4W>thBw&UjJ^IKiGvUI?mGOn^pEh;P7`scImi4|D3^y1 z9>n~@0_Nx9LIhYuoFo(uzNvlq$YM!*&5c(QAB~OuELP2c#f2v{u_W>&o)YaG6h0Se zG0UEpb{W$<wM26*LwSYqKObW5@w}PiU+z`hfkq<{ob2inA>QoFCG#pPS#aMzT%ntl zXN`84G_{3M(ArImVVrYG%0ko1!E!+lD~be37%K|*oUB;*hThw#LM2c-hgyR2YI&z& zsG%6v&byTJf(n&DHC2nJmQx-UfJ%om&hN%5d|gXWb(6)(Q?+~L+4F&L_>M=R3RJ0) lCDFvTYMjgBba($NzySHmcMDx=09610002ovPDHLkV1ms=AuRv^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-center_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-center_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bc725fd9bfb5e64d6af3a686c9ea1db3f98d807d GIT binary patch literal 1288 zcmV+j1^4=iP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$s!2paRCwCFmd{UHM-a#7?fY#9$HlIU zv2kiZ)U<#?KqXNkHK(eFM!j;2dZ^Stp{J<*C#oKrLvK-!NmVITDkV|_R4N$U1h*04 zM?y<&H^ed6#(r;iJG(D7U`!wfveNMI`>~&$neWW5GM`R&0l*|$5Kev+(Qar07>PuJ z;Yj4fiT~wt!CWpksWHxiZ-qj@se9s3!P)qCBdBo>Kq&?O24c(51P`Gc7^46RIe92& zAb1*Q3>ZZ`VXrm8-|Oqa-)Cn(EIu!$D5F;6ekc^0A0HdL7mLS>2cV3D;Au=i(Np#s z%$4QkuBoZ%s}_!li4Pzc^n_n3l^~s-KYQoSFR5g2@6Q)5oKGu?(gH>(vVpcC{<*mL zPCH<&UbnJ~*-!m`zcn=UKBNW*!L%&UHBCN0H#P=8-nwPs!aj<2MV@rW;+vYLa(D?B zJkL`UlF~By?A+YdS9x37D#6tBG$i}_yc>JKpIo^D*T1`NW&U_@xg&7;<|)6=)ikMD z&r=a+>blSx1~D#Pyab6eXF%6AG#w1XXij|Z%g<n;dlYtdYEm4n;L*{u@az5iL)+Wi zKM|sEpKZI1yrPi2@G5P*5vAlf&c1u%eZ$b9>Ut@u*Xz5_1l#t&fk2?~oKz}RRV9RJ zy3WM+*c_--6h9_Z3Tb5-<_lZkOJI&`^#jkjX*U0_JB~tjc8Fz~imIw0c+yjsG9b!f z(1V?7W_kuXM<(Fikr9N}+U7i;F90EG&2?RcVQR3fBu9EGesDB;gr}?DT!Sr#!ct9v zw-ZT7Zmz+?j0^e9Z*VSh8aho45?_2J->p`vu#i~*+qRcP6sasr=5f$GA&H!}@zfPi z*Mr7Bza|B>ar-v-!y$N_r4UZ^f)NZuY<wI{!<3@hdhr70=H_t0tcDHZE>6t7uGfHb zAe?(8gD>qfsWiM|cqCYO{2-IT-wvcwgYXaL6^kG1wgxLpOR)a*31k-1La|V(luPk= z><7#t#sxTDg=o0o2%gNTphFueWwQ_Q&;EOTy?tQ&{m^eK;O_{)d)OQnvsuXHbE2f4 zc66M2+}Rm=h$U7d=q1)CH4WV*Dq&t5<hyOKbqov)e2+OLC=}gwsk*uO>^y$d6YEJx zadr80>HR<|1*>aoV&O}PMB=9S7CqyH5RRoqP1AI-URa|-1W)`z8S?`ckH=S|(eBky zIJ{=r_H(Sao@6o!xMB$tITlw<(_wgc81q@}$KD$kp+uN#`)tRuY)60<h49_E8y$(# zwz5ZYaoe(-M}IxKtg0HQSoDQ_UU&i;nz*7z5^UQ(C=~J^<1X_pipn)ig9>jORj_xb z(HhD+Wbi_v&{M5e2IMA?T|8<73ZS4a_4fDQ)wwR)W3xpzyVlG7wDrN>5sgMM%+aqg zkEC2F>)6tCT)A@C`y!v%r7(>uGtt}Dp6>0ZulBULI?-9=Dpi$=&>r<ZrLID$sx(5p zdwOV`9FDPr<HyW0EyvIe<v1I?s;Odr-a5DRlpn*>hNv)R!j65y;POm#FbZPxE8?CP y#FM~@AI!?-vOI8$oZcuFi<4+??1cZn00RJGyyJ)31ITay0000<MNUMnLSTXk;%yiJ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-center_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-center_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b8ea865675150a76d24c8fd9d1daed6b9441c771 GIT binary patch literal 936 zcmV;Z16TZsP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#N=ZaPRCwC#R!vM3K@@(ayDcr!_Eeyf zP)jQs`Nu>thDc(N=*0sUkH#21crYO`-jJJu5i$P66J9-Oq9JHdq6PF|;Q~l$BR>rw z!Nj(-K)ZjwS++3J(p@#`L0|GR+c!J&^?UQZH>;B*3I4R@z+bTcpug3aohIDQJy{oC zzH-%xLmT>ObQ?AG2)+Gw$Mu;^>w+`Wpxa5k*+3Dn8i#?rJhQE~wg#*giyU8BiNOb- z590CFFz(yh-Fc%2!;)suyZ0X++X0iX>akiaw#teMD0Ml(n41eMvz4^Im;De31S34p z9lF<bD~KU61x`SjEEycgmflDt3f{pXC@ywv1SsI<rsJ@<xM+>V;w1g&(bv%h)iWj5 z&~Oxr3JZZ{8FUs52E*pYm<f4YUr)ehwkY6(b#?IOt@jK*UQz%z*=7Y5cmX($`z|Jl z5`wdGocxhwGPyNJwlr-3=jZQAg;{h1I<L42sE`gyY<^(@LZLZuxk@3CNT$t8M&tIf zWa#qD3d+l%d|G^H_&vDY9;mLWM9@UWwad$2$Ph)>QLYXuQcRyc@09@};V?LMJ3tgg zxjg)SKa`X>!E83esgup}yU8R6zR^)~)_rsWv@zv*;P?&D<`Ye_p!#18fZc9~+1Xhr zM3I?HCa6NjMuSln)#}<BjEsx|&+*YfV5&_4lQg$M0okO03FArsl>EGZUm1kv=HN*E zVfjo$p?Ubcv;-3$C&B9-gm5?#ip5r1dY?XtDyrp`QJv}d_(WRF<8jN3tu`ANQ84A@ z2ViV$Tv4y|PLjkI!C>$)1xS>Z&|0UBcy6p9@Fr1|Sgg@j1gm!x70KeVVCy67J?>KY z^l2JfU%U_7-no<<6z7!2z|kpI>%FAL^3Gnk=(1XEzOu5tP*7mUI%MS<O*IXY+F?{Q z3a#Zqch{i8j-y<pdoQDM3z3wpA>cKJ(Su&Ehw#@h=&|_8GlQO^&jAL16G0(?E_}a6 zVV9)45N)cr4BjY>IK0++SK^U$-nrS)PW~*-@Mo_!l4>()bSkPB72&KZY}2kbMk9e# zN0F2>QZs4W8f{+=88+dSlqEF2FJuDz5m34ee%W;Y%*XaO0R{lEaD?$NjeJD_0000< KMNUMnLSTZMUAI^O literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_d.png new file mode 100644 index 0000000000000000000000000000000000000000..d005211cc7b088e363960a7b3132a7588021b245 GIT binary patch literal 669 zcmV;O0%HA%P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!KS@MERCwBAoHlKm0RscWdLUL{paEcH zWMp7wW@cbvVPRlnWqZKN#u~^1vXGOVO+i^%g+WkIkb#MbiGfz&-MhC6Z{EFM53&?& zp^mOD11}#R*hRDgfByVo*t2`L0@zz1AJW=F5CECa%F4<BwiFbkw6+ii*x1<_m>B2` zSb?6y>%HdYW)yx%a4>_WmKIjEJGO0OIDPgkidb!JEoN#4g(@MJVbh2WczAh%F)jxq z1G)$A;ItGJhgdWD`_~L@v+Edk9^TCG>GKDyYD|rdac3QTmSRgZU<(&CF?{*^i9!6k z5X07EuNZ{5#273MY#I1?1yJIcYQZ~kc`t*XlQ)Bgv>F3Hvk-%?V=w~`HxI+LJC|t_ zryyTSh=?*+YCAG$%jq%PfBXO#RICifI%W*7-aRM2WFo~<Awf}wpMQTaeEI!};rY8~ z3^yO&VOX<!8H1{_GQ-DT?`UHw2t54sfT3=BCBwA`_ZX7gqZrQIJj<~1;93S=R$c~I zV_yadF=^^sdg;m?h9B%d8M?CT7|fI%8F;vO8J_NY&JgbwN0fyGExms28b%5(3oBx{ ze*Y@N^7ZQ&HnMMHkP?<+Sk=6NG?xQg4*#!SzWg5>XliQuPcFcv_r!@4|3^+SNH8HG z*A}E!Z8*WqnKN+4DJ=jvPFa{)=pU!Rb}0QVWoKi5@czSlaPyGX;Q6y>AnE}ND;rzj z$4{Tv@87>qft8gF*a~N5U}j+kwPt8x?*kwX1PCwy{F``*x&Z?q00000NkvXXu0mjf DwVob9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_h.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b74fe417a7aa1e9ec0b577bd71b51cbbe28e1b GIT binary patch literal 889 zcmV-<1BU#GP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#8%ab#RCwCFmd{TUK@`W|Zk8f!m8Ats zT0$zcASV3K)KnBxDker_V(N(#B-}ix2mb)ltMRT;Q>7k6<z`HbKtdBu3<u$W1P(^h zu0W-Qs%=E<%s8{9gtohMBWYhU+3aNAeD=Nh?t8<{&CWIezyKW*l-?3_j0p^Au~?)! zi>2hmSTrg{qtO8Y6D-x5%|KAP<WR<0U0am|LI4m#K+2F?Ob6ppC;^NS0LPaeN-!{< zKrjZ3D4xeT3j%?_$liCC%Qf2B*}1H?*~GhvzK9(0?427P9`+YNF#*OCFoV)aF)rL} zaBwikF`ZUFckc!7G<{5G--E&6h}K%h6DS*y8j+gTydKl?WxPzJR7jlK0y_V{rshks zk+M7^nhK7~#dddfk0KSvcp`3iHvaUC^H^)x)8mzm)y9H6TCLWQnr}w5ijXqt&5zXK zXj#s8-2Aa=c=Y_iCCp(dg@5vsAKo@r@Rjm;w?`JM#9jE=o}Ou>5h>&Gm>@+_SjPyC zKk@pm`b<u{gEv+lfoqqq3pT6GK0E)ZPLIFBp*)_E)2O&ZY$Il9wl_DpI^57|Z-a0o zQdPlMfUD~?#5ZG-9w_55o`5im6hSe`)Euta-cJ94<nLb)jYVN)^;^xW*DwA$TOH|b zyd@!>qGP;_kl2}W#T`~+GTgg=S6KhPe$;oxXP94{Z<~2Jlc0OV-5uxV>dp0A`zo5i zj*v*^m(fH0`XypKBpOV&ZrtQMoTs3=S_FAbhOXl$-<u@!7D7e-Yf5E2MZJs;c_lv_ z4%?fWnsUmX$KCfvUW+t53_P~fN+yZt_@&!JcRnhW2I(s#%9&=%V7@IbEe<s{+WdJ? z_7TguWv$S6a%yViVt>D1vkbDzj|tJ}3giGCg=lt~jF2>xhYnB%wS0GkM3j)#EEEdu zwzrn%mQkzCs@mEXJ}>meH{zcBG^%T9r=Jp)RP^@t`qdoNto=lLPHWq5ZKj{@6KQE~ z580bT8KFv~q|A{jXUZ<X$z)OzMX}_-R4S!hxY?PGB@&4NI?8s#|0loz3=>5wK;>YD P00000NkvXXu0mjf6j7v5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-icon_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b50616a38e0cf1fcb2076bbfd297c5953e37c23f GIT binary patch literal 746 zcmV<G0u}v<P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!j7da6RCwC#R?TY@K@^{lHSO+7T`hu# zDk+8-#Df@%q6n#ffvFcgd9k2q(Tnt`#gllDB7&gezv0P5NJ_+e)K;)WOatbY6}#Qt zS>Nhg7{^_9S5QG5cubOc@BL=p$M1y(K|ucWV&pHZ|DeBhX-Nn7A3RhmY<BtXoo1Zw zwHu4KV7n>*2D()K{%-w!Iw%@)*tistZ6H2~Q1n93CDh|^2?zEu)cZ+aONm4XIF8N) z$`(j~A}@;mBA?@^znK@5p?E0K&EuLoK%MB!L;oyuPQx7HF3>?3GBR`G<aXGugS0?8 zuU6Wd5~z=|gf_8-N|P9=RGf}-Siq3FAz-0UxM!MXao6Nzn{)20t*&-7P3x?@Xns*# z6wuxc?d3vBYZ)H_!=31car@%@e5rh(qFI*3u3Wxk*5}Ti4n%MeeNn~Mi){1sX%t!! zzOq_9dieOU8B(cKX#Zn>H=&fz)=qKV(D!4isAB7?MU<5szQrGMXS=RTZ2L1E3<hjC z{Iu`!lc##C-Juf`JEjqoOTYmF26=P(!<svHz5$`sN2Af9GxfUJ?RLrAH?MoPZLdLI zFSMJDVbbd|lY_cG^m=<NYiDU;;i^_Hmxy8LWF4G2j<eiqJh$<sUYei8mjVseRN8~Q z))-?|_;_S$YHx94qyG%5_L0=14w8ZQt1oR}eyEh!CibWC@_S2g={%}d0W=t>+?$t? zxWb<TUnHb9iCW8(^q6#^+}s<HgHT$*OQ}>YkwbP+2-8-=XcK9pO^H&jmKnoop%T7` zD@qASo;YD)IeA(p8fTg`-m;7~ri{Z}h2-N+nRJOPqm3&1xj;oiD3j>Yj|0h^(|_*Y c;8y_#0R55}4>B@Ld;kCd07*qoM6N<$f|&VbLjV8( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_d.png new file mode 100644 index 0000000000000000000000000000000000000000..765d60e8cc9c2cf4d7bbcfa57c14c490c783d5cc GIT binary patch literal 571 zcmV-B0>u4^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz-AP12RCwBAoHlKm0RscWdLUL{paEcH zWMp7wW@cbvVPRlnWqZKN#u~^1vXGOVO+i^%g+WkIkb#MbiGfz&-MhC6Z{EFM53&?& zp^mOD11}#R*hRDgfByVo*t2`L0@zz1AJW=F5CECa%F4<BwiFbkw6+ii*x1<_m>B2` zSb?6y>%HdYW)yx%a4>_WmKIjEJGO0OIDPgkidb!JEoN#4g(@MJVbh2WczAh%F)jxq z1G)z<$(EiwbDClLlqp!{W=)^YaQfs4;!-g_Nl+XjgPfdPhMTu<Gqkq1GyM7cm*M^U z_Y9&UBG8Qgj^W$)?{KxI#>V9N6g|;AeEgVU*Nz<wPo6#n8fpa2b0GHdW5*ce<>fHq zm@J>7gQq|XumM?ld4}sZZqg=B2P7elOiO?L{6!l}uV1@{RXjc>hT-IyGYsFpePhrE zR*A&sPogYcvvw_3@#yGihRYW(GW`4ZkD<G(i^0^~f?&B#c9wwwH&0Kj>_MH72>KK` zm`JVKaDtgLXZ*i<`SO2yffFZA{AXfeW}$yR1-3)!Zz($)`-Ar%-h-Qmv<A<gJp)k> zSXkNE0zZEGyng@weG06sY`|7HD+4nNGpIE~3ws{`aUei|0RXm1_$eIZzdir}002ov JPDHLkV1gyU{7e7< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_h.png new file mode 100644 index 0000000000000000000000000000000000000000..6202e4083f03628b52e608feba9d3e0a6d4fc2ae GIT binary patch literal 816 zcmV-01JC@4P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!(n&-?RCwCFR?TY@K@^`|_R!i2TdG7> zticws9%>H(tpu7ByaYU`mtIUE2ahWC4+w#llpN}<_yN)6)T8y*jUU<8dj+x7gBWO~ z4J7Rz63~#Dao%n=ZqnH%Ti6Udc4zjz`R#l2d+&|#b!+PaLTHwWjJzK@lT`@-eLkOj z&gb(Su~aO|#bR+*B9zEO!^4Q;y62#PvwyHJOOzsnF-Eipu_a6ZkAYG|2u4T{y$7WP z0iHw&K?E~+k+dkJ)9JW<9tZ?76B82!-ex&97uwz1owl#VVzI<AP(l&lNd!Qdr-U5e zEE<ir7}o3ja}Upi^X!qg=GkmE?xYKN62nHAhscK0dN;$#7Vt#FQy@~C7z(!e=Z~M{ z<ig@L%F#_Fm#*g4)gdQaO8|$M;boq(PzZw1@<)?5CNsFP0gR<&@@~0QQr6bqMF=Kh ztyb&r>+h?=x7y#@nfYh)3zoj3D4N~2>7_wh<|!p+ctOc7V`HNQ`&`$L&L>l;m)ge0 z@}-fHpQ0!dcz^RYvod&g@X)S%dul4*zMmls2k=;Nzy_l_<!N5imMtRKN#XZj{<E&C zE2PWw2RsRo>zE)>I7QyMBpQP<J;ek(Wv;t2H{-%Px6Z7_4I;5W7F>8r>HiAj>Q|{* zAXphRQ^pNy9oHq(Q<r(nPyx?iFQBtDXpzFNg1>zY<muxlcYg2gjj_;55ANN6w7L0Z zh^NODqYPTb!6<{Rvh!nSj<+6~34eR>`qgvwLoV@fF8Y4z*3F}K9W|%MdA6oNae(kr z>cF<+>yb$0!1@O7Zfq?cm_%}5;c3{}$+kfx>X>hurZwB!=#|loa#7*7w(alRq4HsQ zx|<ws_EX9UMKBml@C<lk{Q7uHYdcBT?58Jq>do!Jx;gX^)bNz{*i++7;R0N#ROEqy u0nY)eRipjtaHgxeuFo>**$w}n00RI5SfF7rpelC&0000<MNUMnLSTZB(u|b= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-node-lable_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3bcc5e3d062eca0d12b06adeeefa33507c635e4a GIT binary patch literal 628 zcmV-)0*n2LP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!7D+@wRCwC#R<TM0K@h#Yi%E<|6zozM z!B!9iD+}#x?EDR}5=%{~Rw{~}ovoi>z#k9`E5V?hZDKUZ-L7xLEDY<Oxl4=|x-e|A zncX)#^X6@YQp)@3;d{R@{y~R2GI2kgoSxeZTRl45ueyA<ws-b0?eaDF3jY0}_ShSg zHU^jimXQ%y1e2DZ2Ca}^fFN3!bL98QKhq-<8Zb|XNwPLfk?|quJacBA{NK}p`efl) zu~<S+&o>*5tHSuW4Y&@w$8R+jOi1VZiEFBBNVASI1mGFwMF1XCnx|kd<TFj_^eCMb z0(BBq#3|BTdDhf0t5XWFnv!z)t=W7YEfg+<?{DB{6Ue}%jG`*m7Z|9deR_XK?@vr5 zI-pH+2?y&MX8{<Py1mxxx@JpsQ+<OLBaookOH)&6k;b%vvg#hvny~hE9E9r$Y6Iw~ z71AtmTaU3aWHpp*pBh+^G;3jz>o8(;6bCf04C0pgp=d8uWyP6mDGxBMX%d0OH(+o1 zI|cumlLzh5HH=Hx3_fcXZ-Tu~`W$>0EMV1_;Ip6tuAgX|TUs9h&>aP3%x1M&ORc?G z>Q!kmI-;KBt!3{u;*cB^X==wjZ3eW<R@c*JWhs>m=IFIBedv6uSnUlEe{r;8wbIC- zdCoGd@sm7lzV#fYB2q+=`Yebz0Z<M(l>KV2OxS+B&R>hmpZT->A;18EA?ZIt6Dd{z O0000<MNUMnLSTa6Y#2%a literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_d.png new file mode 100644 index 0000000000000000000000000000000000000000..88aa68825caa9e15b60d97a7a532ef2064c7471c GIT binary patch literal 1257 zcmV<F1Qz>=P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$i%CR5RCwCFR%=X~RTzH0Yo`RH!bS<M zt;Iq?uxw)&7ub{q#5kuIgTZ9Fh{jA~B0r`xnuy__X4?+}F=k72aZ3a<)VYiV1EVv; zvA8i5hPV<?0xfL4u1PPn^_){%EM>GN*0(u1=R4=^_k8c|c@Njw*;xUghYT&!ARNa* z5Cn*#2w9S+C0S|^Dd0T{SzB265R^(Kc%DbP#Ads#y=AlaP*4U~R$h+GtSt6L>5`?T zCH&CWr)6eQ3)2lqg}z^sBrs6w(sTnxMOG-l<9`;3826unh8-3-KYRt&ZDp{i^;oE{ zgsbDd#B3&BO$qc+%iC)wh;1H!yw(Wkjho15dl%VX9mBd;58&44XEFQ!%LtGJ3WY+; zu}ubermG8=f4+)|u`v`Vg1G(Nm!$UMb$nA(@C;i}er*uq{rPNf9T|e&2y^p6a9h7X z?vwjT5{apS4h>ym^XXHk@OsT=%(Q=ltZH4HPSEXvhxjZC7?liTHF)wL>&1R#)-*=K z5Dq5?oiv--{L_^yXfW#FChOfNd*gKG`p@8M^I?p4w4(IQ*7#WGY=C3rI{X1Yc!6i2 zYsU2Q&=BknCtG`cK3upo3>TNE7YK&g{;wyxamP9vclPJCQHX{Tgn}UigFzPCH9=iY zC-&CYvvp;K5rGhSIl>8gBd1S7uGAo?%7&@!o!I_hTMOnUW}&Koo>@po(wNo~z&&+! z_`bInkJs!$UeJXL=LX_*#?N=-x4|*IbgmoD88g0p<1jo2n_!=sfyd&2vH3-0YKkMJ z!ttyDq)bDUwL(jPn|y8iGc~b&&w>pFwUtnnY+@t7_G^sx4r6Qcb9lI6Kkm)RivZD1 zL$a4orB0(=hbDniQ;3omo<L5C0oldH@GJ#kb9?dFTgxup+xiK784^5!5HfOAtgXp} zZ)I6xy!)>#<;X9us0{l)_?R|H_<eqSRJRL$k|3WQYKhZzlfA*Nv#4x5fZPH#6bhLE z3V$1r6m(*IJYi4Z1z62DP*lAWKmIa-Mf)9;7U__ieIGktuh+BLGW9z)Y}63fii~qp zVkF8&UESSmeRFb>^+W0|I;iODo`q@l1Qz^ZEKFOWS|7rW?b}hcYd5qy9rFmySXw5m zRFYY0ou9*}$B)D7^|I%#o}Q#3uq-ZOWb`^z`T5Wr3<QK@F6CD6=3PNMJ33;3RE`}v z0`-HsNFx>nL<zY4=uw>S>%$gO$4@=I504ltAdqtkNaILdE3#*PZO}eamjkAYDAa1O zWz%Mq8I5>AtwET$hz^ppG1*yFg~p~PWMwM}>ntD8nxtbTAOjVI#H!h-R>Nd6!S4?+ zYkhuygs5rylhhZ4<!{}#<59v<VFUsJ0xGc5qTYyFxcaq4NHo?^S{e&J>-MCmX~t5w zQI|<8t~OMUo-;BhR#8Ku$+jvexrg5Mkb)>UMT{J1Qml4GrF+p{naUMbRLF{HyTh)f z<?zqT86O1@Q>V#fGCwy*r)grT+~9P%dj<yjwUQ(=$`V*CxOCjhrpajdOMn3YMVyNK TH8+HK00000NkvXXu0mjfm%>dz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_h.png new file mode 100644 index 0000000000000000000000000000000000000000..46d471f2daed99a73640cd16074dce7cfcc07d0c GIT binary patch literal 1491 zcmV;^1uXiBP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%bxA})RCwCFmTPQNRUE+o=k#{#Hnw)| z(Qe%f_RO|AR5bVj8>3-~F<}uu05QSD4;lk8#>WRf5R4cDiBUdi@DYg`&0;`}5&@%T zV>qWc24QSvo1<>+y6$D^*0o*lJ;(q5mX)<DXiUa!{<*jJoO^!%_c`ohU!M&CcA!{b z?XLhzyFx&#)oLlUTGyO7<M&(qe*X?dNMXq@C;+LcYYrujZ_;N`qy*434dfm0tw13j zLL~^H0WiMyP$__TiWCBbhIlUi_hOM5pK0`~W3AztsRp5`CM6>?kXy0-Qq9gi9Z66j zfp}aH6fIR%HLbk*@+;eJHAWKhdrTKTdTV!dX=!6!+b&b{Q_n%=qmNtsqklSk-#geA zjzm~27Srbvhj<FIK@;h%{QT3;8YU*Z8+&^%bXMjX+4!$Nx2&=<FlSv1&f6Y(>2)yW z7bV_jmsUad2XD6wpE*8K_3*ZV%*+e{i~;c!-BL^>mIgXFI9N~jZ@>9^^G@fc;c?f1 zv#`7}X^x663&qqVAYFyE4#-<y2fiU!ec}C&3`v=-8jVJwTS|V`&~(ODV`t}PtLX0W zcv^h3HQvP~4IbNfIBCx4`7Ze3=n?q+#3%5;_T5QiO0F5^hlk7y3kwPvnVpq|A(fI# z5qB?@%}VL$=(H_Gqk5OZ|E51K57aHjR@bts3842qr@x2bv_EOgeboh!oA1GRvS@UP z$M8-(E~KVslt__a@Vcp{wziW#wpwldnvg7%2HJD_JIE_710{PMocj3P#QRep9)U5p zJ6hz}bcJA=AY*WC<S9fZZF!1!TU%S*TTx!vyVu#+)YxBW(D;?kv8I)CE_M9`uKs@5 zbLa@n`MmJOJBJ~Ba34%hcp*4D8?9(=>BuRz2QhR~&migz$WurINm`^yBzdDxF~)_e z#n6o!9NPO#VtgSGfU4#eD6Ov7rRv8saPj<ksBCEL*xcH7B|9%aO0nde2}LmoWJ}_4 zaU-VGEmDQLM;yw8#a0?9uH0}=W6HT0@b0PUnepojA!yz8bV7+IpMMbqH^Aaj46@C4 zMHEF5Mq`GSnUP7Es}YL?+hDw8fU(;(9cf!%8z`%7I7?X`4o8$@Z|;AVVWAy8^hVOe zFnVKTaNt45mYK^%CHBRvER%{*jVvGz@${Gy9n!RXm&>JRudbLN%Q+Yf1~GAMIH#b* zetCR)&M`kZZ7Q&sN3u+rL7Ho}+VpRIV{UVCX<3NoQ%vIwTuUrB2eCS$Cr_Si=)2J8 zq-U>p!m4)vVnEBJVpR0WWsdts$GW>~BZ8T|^O2@3l}*|0Wdlvk&BNst71zllRC?F~ z8RtCFeB#YPk6*HtQ&Ur>-+FqsQGL??tFFPVD7S6;;c&Qka&j_iwwV9kP*Xi;;08T` zFm94S%gu*)@s6Y=TYBR7apy9SuC^z(zi+qOgGOUKF_z>1WqbRUzuech6c!crI~;fS zH8eK*6?7*@cVZH0Moc6Eixs{7Mj}aA`LgS5SC!Y}*_@k~=PoHJ87eQYm?$bPUO*)n z)76b7Hs1RBx)G<-*_*R2H>PST*P9HwQlfQKD$Ibr2iKP?Jek-}SH85`Y=Qj;4!nk4 zgrm!p2xcm3$2eM$k&&TN87$A;c+b6FRG79d9K|2!61}0JH;~OGt({sGG9J@pORK9l z1jyZLEZ!%nzPN!)B@Wdv8<P@%6s8O%IE|q@B>|VmWJ<n>#K|FLKc#sLV}TI;3w)N8 z7^JjFv-cuI=o!jxX6P*oUf;x1EKMv#{nEhXO+^IAe>uI$|8Tm~V@d~L-0v(oIXP>7 tFbjo3`hlBb+8zi5cA(s`6aGH}3;?%kZuT{5^sfK_002ovPDHLkV1oWz#Uua# literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/object-transform_n.png new file mode 100644 index 0000000000000000000000000000000000000000..065cb1fc73d8970e5971a379224b453fcf725b5b GIT binary patch literal 1015 zcmV<T0|@+yP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#nMp)JRCwC#R$E9^Q54<hj$_V@sDGJ{ zu;?RDu_?49LJ6}Ev4kjzpezxh(1hs6qA?QsFbMpJh)Ar+M@R*kkyaMvGc_ZnbfN+C z(TJI&G>`jgA3b-tGmfJZlznty&%O7YefK(howfH|BFi%T?ZLqcjQ^m2bOd=%Y=~PI zaFD{&XCerJD*z-1A0yB#0CH8u<?_<s%G8*gQt5BK;ACM0&%0iPg@xy3?#hVRlbr== zX*=R@1E%9er>f9JgGSWv5bu&vXNrJy=l2OA>ywN|^o7ITpqOD2AzV&T;Ry&e7~JzC zqhnB0dk>sW=fRuTuGZs}fF}E-*K6#hKe(}Cp646zYKn=8@o;cDT`)0W^#G(JI%*A2 z-+_UC8lwTtp#U!hn$E6AW@ctOv_V=Z$Uo}gF!1pc+^V__&CgpPV`sX@nqIF5m&>&l zZ&#s-%w*2bS#H8h9D5A^J~^4~a5xtDNumUFzUgTbOxtW8YlDM>6ttb4YmlNLe?p=v zyfA-mq*{_B;tRB?xfxbRMuJYSgS*vr?)jR!dN2<S*?5<$jtPk@S~AM$?doj7%S+7I z#A!6u27^Isx7%~oKHa^2@V>7P%E~T+)j9zc6%{ly7#kY{>$h=RNJxn3@xuqhln`di z5|T1R6bwf*Ejf4IJ^5p53gQzI=R`w|-nX~4V%GXno)>CgJ$qzhKvY0H16~qTX6h9s zN&JcwU;1n|gGQs#3WBg*tJS6+*uT&1oPR72dV9LTVYfr)o7dA!I26#yK;<RtlnF{E z9GBVC(cY=5wUCVoiSLlU*r@1e5C74T5h^U3PNz%8BeJqm;g?M>0>=5GdUMLw$l2N1 zJAx=4vREwPNW~M{58Q2z;{bIKi?`!!qoUv5>(z}+^CZhjTehVJ(*Uw0(OpAmXlOIC zl!dpn4s%iA{m4iy&NU({!z?SpDugImE((f(v|K7#+0ZjBmqpS4;mdM7NYZWu{(_HY zMyIF-TPjnN0ysyPMq})5ZyRA};}!(cd=|k<Fv}YBx$(BG^#h}m0=iVx%6>sHOWMnd z(ONKA0|x8DVB?G?PW=w9H3~d`9R>4~bscx33N%v-1|e)sN0rLXM3KIrIO1ly*jg|H z6&Vu?#Iu~gHX2CF8a1b?H{M4uWrIYOp%l=UgSs?Wje&O?^U**Abu$V0b4B&C=#L^_ lo$H_eFe5ARh5t){0RU2ji?=SI<K_SW002ovPDHLkV1khB*qZ<V literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_d.png new file mode 100644 index 0000000000000000000000000000000000000000..48b686e75e476af2c2b4d0171b8a2f72987b643c GIT binary patch literal 1199 zcmV;g1W@~lP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$QAtEWRCwCFR%=XCR~-NKz6ycXwy7{6 zm^v8q0mwt=6kBAz;-Z;M-GdMZam3AJS+*?8w!|zhWJ_F@Y}v+#z5Jk&;IiOm)I<@M z4RHv{Ivyejt#m6>ORL~`wYS|l=R&s*rR|6ApZs&~J?Gx@JHP+)sA_6z@&MEmH6aF~ zQmMf6JOn|2BuaKs5=#XN7_XL085x<->-FF`4zUuqd&J~&d+I4D1I*6JfkvxkCyJH$ zd_FXsJ7;3FD8pC-Qla~cq6h{`O&V*!S&`IgaQL4^B;=x|cjlNCSDO0a=ye6>(w7)f zQ1S@2maPaM&D5%qg7tO`<D2)+Vu3LM`Rg;WuX+nKIyF45ztG+|Nc0*Sk9VNrz$Prs zPnkCh<^|ojYDdWX%T@1S$KFC*YHmmSFBcIA1W@$ET3DXSVtV?B6<@r4279YFV|ig3 z%g?AmseD*{2>RqiyjZm#DUbT`QrWAp4gMZn|NNt`v32_fRKBtY+bZ(e+>!Siaj^DT z7OCQW0#rpRQlQvu#<J|}uuPS(=kajHB4`(A6tiEwj(cO{A>S0|mLb<-!lq|(pq8W% z#;vWb;iLDnk3T`}w}(+!pd4!Lx|LY@SRS6Q+>MP-mcVRT4V_-g)(*b=78_Ss7Q_0J z4PLL8s1JU>KRWZqC5R-q1a$TEBPDeql9Cr<*9%VvnX1(irpe~#&o-mGtB1{H7p3C# zSC`?J{gC}K@qH*vSf+x=gA+JRO-{jJ&>>Ni5Co)>^pFwhi_+Nhh}+G^_(Wm0+B0CE zPmT_hb_psvoffxly5Ju1VD#=-aNTjkiH`P8<p4=WHjdqyQa0yQEW)Wq1?u-JKdfF; zfZMk{80fd**q3!^IMsv`wI|Wmav1{ycErU=Fqf=j-*#SbAT>(|6_f-27c{I-ebX{z zuZ*&a-LU@JP5jo6(b0Rb+lClpr}HL@-tzLD&}ww-pRNl-$Tcqp;mCkztran7)xQ06 z<{U%AO#dxMkABCLnRjFoMcXo&l?0xP4z!`|1STdXF+MSl>1l7sT6ae`QqxnQHyVO- z=j(cK>C|=XeC=@rNSaXPB$4HIR7`0NrO8clTrpj%6NA6~2}hp`&b~gpd3ZalKReLb zY-bG14-~_+#6TU%iaXEo(VLf^GCnRIb)UAN<@??_>xU{&A}z~+yy6UO*tL8nMSLt> zB*Ib@g*nAPEGT73b<Z2?QU1CGZ9m#@?TQ08uHM2QeQtd4!*dKkB!`=gQhFia$vdsR z_bd7W&qv<J>881B(y(k*GJLX30!3z}4m0K>n}OpMlTwBc>@or$c^{{~5kz688PjUR z5!3Muld__FQFqYq=Iy8C9vU$y-H0k}S2ATvm{T7rcU$g(%;tcqCAHmi$77<-kLGg5 zd>v6#{3F9oCmrmBsZ@G*bgaIqsnH~g61jzoEC>KqEbe7?qNRTeFaSv^cE(ul@;?9o N002ovPDHLkV1fpfLp}fi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_h.png new file mode 100644 index 0000000000000000000000000000000000000000..34f5439d1cd5aa2f406451db98b2e32eb0e40383 GIT binary patch literal 1414 zcmV;11$p|3P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%C`m*?RCwCFR%uLJRTMsN8-~IRk8Rir z(;}2Yiv?t{4Q*4U!J3N1ipAPc8(K{pm-s_t^apL?A2l&noA?70O>8W!F>0`hw6vtr zR0}0Q8K64s+psS)Y%_1}yWV?;fho)g{-7s0Z@KTj^WAgKch5cCy}G-R08mU+hK-jg zq7@>>xOj8SW;rY(a>EhFhKJ<-f&O9&DhG!d0ITYTgHoKn{yte$RSYU}0_HY8wip9Y zJnAWVP=R|ypcGG}VP&A}_^IN;Uuo?PmR(aLb6ebFvSCwqLV8qF@!L-}t_ez&V2Oxa z5JLY6T3g)}d9A$us9&IFDj_YhDYqc;s%+5OM+R*EcYZY|{nIdz_TiC={fADaU(Gof zZ{a!KCn&`u;Z<2qxfSs($}cVRd@p#qwCd<PS$A(#)<oU9-Wg58#N`*}_2wPhW&+q| zsw{8Ls{E?H1R>?S%%`?mG?Ii@hZh6I6IJG^_ndzE(I*fb5eTPFp9MMI+WSVydnYC* ztVZ|u%Acwi9^bY7_cQOFtbOja)Y?k84^`hbzm%LEei3Q4NTbo<)kGp#w{BLSOHNK% z72a10&XlcOgeVFS|5zO8gLLfuL0db_*ybLZlOFqgk8ww^DZgY#qrcV<N#e4{^r5Y- z6?nI&EVmv?<>KcT;PMaOz!Moe+54W{OvubmgO^VhLEf_mVDG*hkc0K?+xfEh**z{r zZtgLSr_RmI3-j{}yyA2&+eI$YlJFuj;GIa?00}gHBA8mu5EUH`hKO)@@x%*Dp8EM| zz@aE`^ZFmqVrpe$+xLWaw%i^*tSAo7Aqt!z2%NN-UwWZ<tT^~$uP6z?i#*sD><|(n zL!d5T36P2*G7MtlV%YQe#00wwTEa?j<<8-7@Px@K&+{Ijm~I*56V%OUS=PhI@EA;t zPl9#YwglYQGYAd$nq3Ppgt5DA(&6V$A+_s7PLAG}w+sRvBbTQu6&6=TC1-aAjE+u1 zhq(*BFTVoS*J|MB%au@frye>wx<RYez}^G7Y_7R>JRu-d=%f2XrM75JJ?v6E5wU$r zK)kr>bdZS(f@7~1K~rN3$(tFh)*0yT>SZ1e4h}Q+9zT8r^g7qcX!(0KB|0h4q&N{r zVvAL*GVk23QoN-y=uJ+)es<MZU$2F!r(C-9BU37lSrn9-7v7>c6;30v(oQ^Hw$I8S z3xO;556Nd=pF2M{KQGv9Ga9?yE_!}@&}0seijA-ah3M_>vBs+LjKSvl-KmE{e{mA# z@*Gf5l_PQXv`_Iwl`IAMJWDyu3xX<18unLv{dC-D_gt)PBrvx9H*4I%H-o<DsGCk4 zGSBQ(X0bk_&`=SR<UdN?W5MJ&Ol^4DS6!zJ_IZGlG~-QLtx|oZbN@i=%)XW1Z+zMJ z79mY<bfUg7`*75KVw_hLg{Sl&wbv=|yh^dLiao;Xv1PmoB?8Y2_?gng8Zx=2>Gtqe zOQ&_q^w5HFb^;j-KQMkwx0NVKgrrW6dPspp5*<=95&eIqZHnh25?Z@eA?1uq$kO#1 z)BOhsz@H>H+=AUMkko-h$wBfb5<rp;ERbh`EY_P{7m;Y^fY%14t0pB)RTuL`$_z*L z1SVH3Ng^WN5+I(=!PI+SHeESo?FwF3yFm>SrJ+>Wf8<Q%*n)UWJjI?ZiSL#LFV^1w z*$Gk)8JMfGE#_){He8Jv;3=DRTGr`w8{U}NY&Oc}DN3Y@v9YmYqK~Y@|5tzk0DDsl UNPyABR{#J207*qoM6N<$g6#vMYybcN literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/orbit-cam_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0e34db316e928a0076bd39b6207cd6a93cb935ae GIT binary patch literal 970 zcmV;*12z1KP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Y)M2xRCwC#R(nWOQ5gTu-Q7cUYb~8= z_*R09Cb|++D`AR7P*g?_K_wyxg5D2^A_yX-cSK-N87fgk5QV6bp!r8>C8-HIw;Bm% zDo%5{yL)fv-kpuP=GuDxQ6GGqbH4BH{Lb%toHNApJp5^);4jF3(BCTbY>wpkgl}wy zm?NclD8ahQ!`y`RcBAF&Eq_3LW>auXF<2T_lM|4uLLv5f7(|A`$0C7ZtFf)j5OdgU zh&z~$zd_&I56#vxhNbIgIFc2G`!LYuq$phm-d2N^GJfo6aryHT5A5a{$uUa7<k$CJ zXvZe*hnQBJE{7YH*TlJrt!qut>Y7*wykHrqW9{%HUaPYtMCx{7H`CC6?h2B!XSgZp zeY}=omHaeQu>fcZBqqgzYK~G|_q^+aiSfyqwGdL!jKOOfjOT?M+g*$UW_tDFC8)i3 z6Ee~li|ge%deARRgRNynU|5p}xhpb3Jy#|APMtjJWI=P)ILzA#TiRESX@8)KP$|gd zG<0_NLR54FXd@$FQ^^K5Q@LCQc7}nc^)1lV*)7^^8dIZUFy%T4ub>XBe87E5OC><j z6iiP|gGQr<FlDG4P!R3>aEOUrAkK$JM#QnD?SYNBc@N;gF9(F2kx{Ev@M-Wfj0}&0 zb!^=2x4a*K*KKc|2hfI#V^+pm6>)PUv?wT0j&nvOGb<g2hDO2IYl51}yU_Uf89c0g z1TS0L!DuvtLLr0Pyd3dc<(H&FK<smmkOr4ihn)qMVPRWo5p=xi!g=cjt91g*rVk?H zfq_A>dW(xUf=a1&#?&U=j1+7Q0pq>%8Fb48BFTyZOQWSiPufK&qG_RzJzHj0rskmz z;iPh`VPtemO5BoEma<#=O+8kZtzhBao!q2sKZ+RL0TwZBg0e&d4=*VOhkN7HJ=wfX zmd!g*HaH~N(fUQnA=bFn{FG!1H%Y}%n(=k@uPVDqXgLDN2Ma3vXwndS=mhZ8an9=1 z$Ac+-JmQ-1g;3SdUw#KO6xu^dfTn%*sNbCWc-jztMe=}0d3e4OA0ih&02;C>Lq=CO zIEan)b>RmCN)Z-P5+Mm0d5@xT<<Q~pm7#+uOx`2E906s0fck8cU~=jW>S98XVxfqg s^Jey|K)tIPe6>R$g8rF5+g}A307WBK=4%HI)&Kwi07*qoM6N<$g0hgn=Kufz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/rotate_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/rotate_d.png new file mode 100644 index 0000000000000000000000000000000000000000..597c85dcbb72b7b853bdc0b2310f7c8386af1c7d GIT binary patch literal 844 zcmV-S1GD^zP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!?ny*JRCwC7mR(3wVHn5%`#NWq?nCoz z4x2~ansY8Hv<RX!5H;w+$W(@5h<DvY7u{GS)J1q96fTg!z$-%$v_w=Cv!pLS7OoFQ zxy-p$)8^D<XVW_8h`X#aT@U<u;pKVX-+A8md7tNq>~{Mh0Nt!;#K}Y=5o9tMD=A2V zbQ2_5F6AHzBtd6q8=zLJAr^}fPYVPBbZ}+0n*;HXJgXH+3I(K6X<R{(NCabJV>JKy zTqOSk!Wp+GNs{1q=8o%M5Z9L=4%{3<X_^YhGS#rqYE%>pyH<%r0>Kwz%ut}-M4|ty zAB|647@U}oDKf#J{=PnF!mogh2rpUC!OsakLLpA}mKLy?g957w&FQI7DH3q0(}84S znw?+4h04mEM>NpZ5{-o0>~v8fjuae)?Q9Ot6&q1!)L~g<#z&V6FUFro*M(m`i4D|# zqY15-Yw`Z=G%{%#iAohhq0p8X2DTC-hFq&K84Q@5nnq;F3tvMm{1sPX1L^ZJFwrxL zg{}_lMFdQU;Q<UQ4ra|!;>X%L()C7!ymN4s6l1M@9Ma?3*g$G3l^-{P>(MB9SZEu@ zAU|0S$Gh)fq(pR`EXNRAGNc=_L1{H`&w1czYC_<|VdzhlfU@LA>)G#c7?Ni_IQeE2 ziPc3=natSmd13=qUBAPRk2*T=IcpN@vrc@t+l)`&ej!OIgM2xHS)C1gwDS-*T*dyX zy4|IBSy?F_EG&RY(IB%T3;QSyTT%!(ojz<noI<du6uD>4@BSSnDU`5PSE0A}F=m`I z04LUlUvHw&OrfTxn)iuIWuUZasH?BXMVpOH0zFca55nWyL}P<6h~UbwZ6l*zv~_Mp zbo*L4bULN*S|<|d=<x(_&)x?y6XrEC?1-&hNCftvTa8^<W8aj;K*4I!!<?&+4MY%x zdvVcEbDi*SJ?VaH-HsZcm(5t#+)_FF<KSQV**!QgNRy-t1S#i%+=GmhxmlI}5nuqh Wh7J1Z(BoVH0000<MNUMnLSTZ%?tC-= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/rotate_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/rotate_h.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0535f8f0a8e3fae238042fda6dd41741dd1dad GIT binary patch literal 1046 zcmV+x1nK*UP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#xJg7oRCwC7R&PvGR}??3McYE#OAF*x zMXR6|)Y&;*am>QPL=*fIqh=UQ44=rdM9pHdee;9MJ{b4O=m*V`!E}=>i)9-GqGmE? zf|>{+F;?1QA1hiwq1FC*^xgZO_tGMiL8%{ll3(u2%{k|H&$+*Q@)R8%9UK7EQy|EW z1u1T*BuT0-DJ?;(EhSlD;eB4@^?2)5AWLZF%3>fmo)sdN+v|2C6(JH}5vO(TemID} zzubBX>GOQ%Ld36HQ4~1cQ02;$jB%)BNdjSV5|%7cF6dbCVR3MIQGVa>i<h>EiI64q zCekP3Ph6RjFb*Xq1SBGY7@u4aSFc>HHjn|UED>PHiF&PUFI~(uSZ>C_!~h~~okB7H zi7s6Dp%G)uePr7RYvTowZ_vT@-~U!?JkP2wUA$yy{Ian%T}84Cz&MnIvN59BIRDMp zR<<@b|6CmxMXq*NEjX$_gQExSaA@x~C|kc4mg+XZ=-8Oed9&3v9XIqxvt@2B2F5Yf zI?P&!XV87;_7`W49o{`O=-skrwFPu2AHtCcL?cm%M5D03b}P7j<4{&+1-Hjz5C7*; z|9keU1Fk=vCuZkiz%#9*MDbMB=@Ihzhxm$6FbLLlr4XgbnJJU~{h*wK7$q)U?*Mmy zFL(R9Z%-`+Q0w)1fw{SuF%A{R1ma@q8`5aBK_UDG@T35OAV4f8OzkU@L<*$>j3^KK zAM-$zs&{cYdAh*dT+A3pq9XdCoFxWhfoVjq*L7hT>|MY9r1tmrK=}>_tg(Ikj*!IT z;P3tmmWO%J6|d+snav|o_%)Jfn^LNglNB+Yi=vpKx|8S5_pr6q=`_52?zhEW_~71e z*WljuA0b!0402=%#uVG3#1sPc*2=C;M~>f}j-}%HpX#O_EFn}Cc>c+-)8XiBYiqaU z7`LH~t1M8&nQ2=!F!0n551c)qT(>!}sb){lVv~h&Bt{UK-%+d83YDK$Ha)n1|G-n% z6AQ)EzH1-`g(j`9rsne&=Ce?I$_PrlBXL2e!h(WeU0q!h&ku3!?T_pRlpC5D57`?U z_O)oV8X;Zm3{i42Ff3->{|qA6<A9BQZ?7n8F>`t$lemPuGpLB!@bHBC(ZeUZC7e){ zXMo=ziWToMy=g@<l?7SkWWO}ss-h23otwMNXEf>gBF-2f8CH`}o=jWErZ*;O9Z#P* z*h!bply)o<lj57aqu{IYR|pvl+4*4zh1eYhRgx^<7#$t0r^xEh$y)&i0J73r@rFS7 Q>;M1&07*qoM6N<$g6h)nI{*Lx literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/rotate_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/rotate_n.png new file mode 100644 index 0000000000000000000000000000000000000000..65530d3a9237a5aaf627175a6514b55e40bba1e0 GIT binary patch literal 816 zcmV-01JC@4P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!(n&-?RCwCFRZUD2K@@(obZN_4N`j#n z1*%1m)&Ry7v_!?&kcbEB#e<2_pz)^h=*6hft3Njn9yIahfu97*$xt!TBcSmICD5P| z3lc2p&u;1N_-2{b)$p@G<R#zEo86iB&HLWW4xyC7R+s==WLrjLS?*=Ed-q@c_RwiJ zP&$cj1o09v67KEkF+(sOoiv|OxTiacrF^Psv=wm^{f_@49cFZk5C~DK0RvhH-`(c( ziJ2H=xm{<J4g(I0qS)y3?FXyX3QngJ`uqAo6m`{~p@bK()ixxZy{IUZ%fw=_GzNn~ zXm0XDAaDqp{0E_?wgz@pRN^wpx%Q4wXD%!BTh}XHx8!g<$S9ffZnqn3HX9_8Nk}DA zkW8hJFaSTMr=hlXA0xYR^h9ub@Z_l{Siiw_5fMR%2t>H{Zgkkq`Y8;a2a=V$%5>lB ztpiQf>XGaV4r3CI<U*L*-e^(w$Ye60C<>(0^3rTV_(F(YtBHxp{5x)M5h5qb8f&)_ z(J10hnXCM2Z&#PI&gX-M#slE-?8y_VstS?8C8fkd@j07Af$n4_<}uQ15~UJ}hity5 z`nV^X&7O_N<1o<Q2eK@KBuV-hM{%*<ZgV*9zZeML<3Uw~jfr7aI%YH$i(PlQTt_Oa zswxf#TA-w)1nhP@%+AihtDzw%EiHZjd2H;lp)LX^WT0lx5t~$aEZDYJl8PRoUM;!r zzvkyz^JXR%>m2{|ae)WT@=R3jFET%Oj#eWg94u=a68bZWa#<)4z9kchOGwiE{p-{M zpTYvm94aT)kt14FIu5onGB@{g2X2Bh`Ugrc8c0;0iOk2+jsEMXx4uErH0>_iX$<JM z8H%s)9hO~*WUY+;Fj0B~A5P4OT@kOb|A7Zq4Vh@0`l<e71^k@Yc>QEUHk-eX4BOYj uPkw>1;2XYSS0N%?B8AJgXqM==00RKhWith4ftViv0000<MNUMnLSTaJ)_slu literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/scale_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/scale_d.png new file mode 100644 index 0000000000000000000000000000000000000000..eae3c839436ca7602e7669b521ad3c7fd83510a3 GIT binary patch literal 832 zcmV-G1Hb%<P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!;z>k7RCwB~R!vA$Q562(eSb6Qm?N4$ zjXC}#WttID7=pM6T)5DnMHEQXBHFbo$xSV4RZF)*ffa~uq9BU`gKAL_Gl^K{v&<Tq zAK#mFW}Y)+_g)=SXVjUT!~3{!f6jN#x#yhwgx=oXBLIddoXC<0f&f{TDI}<hswt}4 zDKQeOsyM5wYhbtAK?p%MWo~ZHX$1caF(OV<-_QVCULGV#$|_Mb8pX)Sh?8q&o@^uu z8ahG?3u!CdQ&NhtuPzL{c!-|Mw^!$xPE}RG<HW+X(Gy0Z8$pCZVT2<QL?R|kbCFvZ zjl~d)#}Qvz;(LAm03WA4Q+V+HX_675C}PVv$&`GNDmDy*8^bO+0YWGQ!fqZO&u<*8 zs{@e;WQ&+m*R~g;>pB8{KX&EhaLd*L^do|ZS<furOGFx(|0QCgTCAATG#W}{omUJz zv#2r4A)+2xcR-X-R_<`1u&9XNX9CQ#fx*FLk0f#DL`-sNW)_#0K~WT%NeU}Qo>5HP zlOLe@zCf}Pyc>Is(tHOhN@`Zml1yu2Ir~gLMogBC5x(}sJ&3-~kD*YHdS2edx7o3E zXEP@<BS~aT&NW@6i9k3CaxG{*+`cm+bJdf7WK7Bnt8lcq7NR1c$#FdCY=%TJN@6CF zC8WLXG_50o6P3-GB||72MlfjbI+>@P!sT|+S=XlfPgb46fyqaFOq+=HE(TWdYb7xL z3Bl2i*w=9(<+&TpcT>)1{3gu*n1K809lSYKkBROs=+Ex2TeqqCnbpztwib9tzr#j* znoU>%1mscdrt5L93BCU<VhmlcP;jenO9LfglZCU_x)Ig`kXiqtWbz8;$KCjOuOFmP zK|x0g%FbVb;yko%BqVW@hpw@;E#-Wmaz9j2K*gC$uomr2JLD2=Cz{Xab+Q^s=XJH$ zHgcpZzh-9Gph*_`<KXoNhK8RHJ5^PNqFOj%qBCbfG>Xo@0t^7<@)aGLPlf*g0000< KMNUMnLSTZaU3C!v literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/scale_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/scale_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c43e55ff5529f94e240e76cdaa1ff87c03f24a25 GIT binary patch literal 1062 zcmV+>1ljwEP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#$Vo&&RCwC7RzYtYRS=%teY@V+Zo5*8 z*))Y%wS(G}wuTFb1R;?M#Gys1km^H4Z=5*r8~7oSka`GGK}85DJpkcCDHIfmHgZS} zE>YUX^*ZY&UOT(*?RMsE<9Z#d%_Sp`-rJq`=6mzayjd&r^Yb<Ulu<3%A8}FVSQv)o zLa|_t=L`FSa=%u!s`sj822p4oJN6m~?)Quj*49j?;h1}McKYlY_xcaltgGK#EnS<v zzR+(Y-_?~BiwO||4|rl@y<RsR$Fb7s45+FqRZ$esXs)R^YVsXPtyY7Rlaub!%3W)@ zR(%hBEesxnFjy3Z(B0k!O;dY@D2l+)4e&ahY|C}CF@Pr55CGE{jtYXSu)4Ym0S{_q z*Hb#pcYzQF<3d0v0@UZm5Fa84q0wk$TN@kp!9#~Y*LA6>lm-fd=!SuS8Z?_t2>fke zOhq6j=Yv8RR16Rxb;wNO6hcH48ki#^bn2sCyFH>v3PDl_3jGvlZfpSO95ki@!-#0! zFX#dY19K#H0R=%IjYJ`?gRQO2kz#QoYuh$PltMKyI#kABc6K)2&$KwAU^ft9Nb0yq z@=72?AC2-E7aurq5XP{A(wQ`5(x`IGNc)d}%-d_us;!$keEaiUeth(}d#3o7J3t)* z`N>m+L>-Tl7D=!Owvl_!^M;o9Pk$}E^T1gz5zzNHZ(Mo5H2wAD>!;oRoU0ri`hlNN zE;tB`c_$)@pD4j9lwe-lgEo%sb`1RS^o3stgw)KWx)-KCsU-aIorrLv_7?*eapgrw zA(2dZ^L^+b&@h=8J>ed<tXiwHnYXQ6B^1v~IE><m<b|c=C#sHegzse~JOiSWdERg! zANQX<{^qS;@7>P7F;-Zn=VUR+szaLiV2(JnS}r(F6E>f;po@`ImX@Rr6Y4DQ7H2AV z)+>)_9PBiN5w{?bG`y0_xun!#`rohL`O|9rbJsp~@zYB3LdN^cZ(sUq&{wYo#rG)K zQ}b;8|DRR;{pO|h#oOH@ujVSb!o>37nTtJ8Q^|CP`4T℘ukc<jc=LyW)FW`Lxc! z#7mjhHK2JKFJ;zXwBBq#eq5@%%~G&dTg+Yl;=30eu&*;DPce1wgByN(%QUcw8t9xf zh1#h<w4dDn_d{$&-R$Vm#j)v`TgLba_hpR8h^WnofrzF)I{#=Wb<2Od4-ve0?)+lv zh~3?((3c5eFsk{Gi#_GYDonblzezgwxF@dSI7hD2bSw)$1^e=cq1B=sxeRaq9DY&D gsQdco<f#Ay0L_6|1BdO(*Z=?k07*qoM6N<$f?rMog8%>k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/scale_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/scale_n.png new file mode 100644 index 0000000000000000000000000000000000000000..fee27ffe0008ce0e2205c5996ad291918e1109e7 GIT binary patch literal 821 zcmV-51Iqk~P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!*GWV{RCwCFRnKb^K@@(o^J8Ovm=YRH zZ&Acz(HdK=2NAuA2R#?Oc=e#D|A77hLT_HY)PwX`Q5q5iwTh%r3l&fC6j3BlkhG9& zvT=8Pv%U>$NYagm4m{q>?96=g-uvcT=6N0*L^I%kniw>B_uhjEbQ4hzDZW^(WwO~k zc5nUK*?ANhNQ0zOhx6%l8iWwQIY-YyO68ArpK}4sV6aiC==C+6*FX=^#sY#=DhZ}( z=GyJ{l<T_Sx(+yw>w|Ix66hK0Wqw@<$*9lBJhIV1x;2~4DbI5Yu~-b0Qav+G<wqx_ z?A2OG0Yof8wf~Lg><ffZ_&1Bi4zWg~ajw&8XPCYyV_+D%$Nba*9j=tgLqLL}aUw+& zG|RFw)6*x8<Z?Ms7>OA(!NkaP3YV9k5s>0K#gsOKJVXY<TlyOk$s}YaCn27QLn4mm z`-H#d?D>UxTvu=qXl~)cC9LJo&s&>c#{vpk*X}~A)f)JX3vnBP7Wv;{NpgM@{eJ>= z5y-NJU1BL7k1x`2xAI|qt&e@tmBF@cT`M0<N7ib#ph*#8ntn(nKcay&)mn%UJgC(e z5!nb}4VM@po-)QRVhYPRloUyi;zNPz^?JYt-&L#Ckr#PWE)}m{zwxE~>Sd9DVBZI; zwF})hwD+MjsF~R_$B@Re+n+Yeg9y6U*u6$k7!-*UfU!ZpiR1aB;rGx3Iz2ak8T~3s z(;lit31z4J?sd6D+d611+M{UP)^C3((5aI%Z;`TBw18@<QKkvfmmYkmC`a0BDbmt} z^DCIHI}}O4f1SE6&3U70bYCktiY7C-p{0hQm})EF{A0Y_u3;F@k@8QlHZ{6+Slir4 zqT05sZAu*v%L%6LJ2HzQ)mOGQDpu%>Zm9SJf7zpI#3|RZU$^M+fLS^Un`&YQHHrK` z>Qmn^P(Nt5zaHZPxuF9F?c=oO9~uv9e*_o+)4B>B-X|sz00000NkvXXu0mjfX|H?X literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_d.png new file mode 100644 index 0000000000000000000000000000000000000000..7c39417de9a13bc2ebcd02d6a5fa01997a2e04e5 GIT binary patch literal 1026 zcmV+d1pWJoP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#q)9|URCwCFmTycGcNoXNch|dXO9eV8 z<$tcAps<>OPDFXJXf#nk>`YkZ^rcbb-jGb=UM$8pZZD>ZFC<wOUy)2D%g_WE0)psB zTvU+g7z|^JKxv^~>D5W8UF+|6G7Q;7TP;uW<afE>@1D=|@9rL^zP|n=08K<C#6cK_ z0mpIBXfzOb!NCi>O+$e?QIPWUSHfsCf@N96OJw<p<dMBi6siJ0v|6zuF;Vp>UJ?q0 z(Am);sbSFw;|-)j?ejbj6-rYYZ{WWoh$2`lv+yLin8ItkbO|AO3gt!l$W1YTVL3pX z04B)<CM9d>Zl+m{7WnhU3kW%eFrz$0vn<1qYR0DWG87PC#5<`fD_Kba#n;+kqm3{~ zfV)1efhY>tlN3bR*j-H2pTh5_Phwj30iqsZ$Ni-nDjJ|NX2)mMJD_JHSfe-*{G=8| zUsfVV@!^kaEf{P#kI1?TtY2S(71NKwm~vx}lma~-4{Ennz>p>3`K@+Ltt!DpPdogF zb^^yfM=3Ld^181f4)vkm?n1;j6?=NGcXz44{rmSJfeQn(Pu2U{+ybOO7(l}D?^K;@ zYlb$@iVTwxUe^R7foHKp1G53-SuEt^ku?7Vr!MAnGs6Fb!IEx0TGs$B`(@WACmJx} z_AF^Lszze8deSI5M1n!+d@ejn%RsQJ5ktKr_`#Kr@Yo>q#3xp(!|2q^dx9E_h{!JR zB^8kDcQBkPL8v*b?sYca!iDqK)ICeG9!_O8cBodTh2r<Wgr;T#!2s9{37)LgD44cm zwB@ITv%fU_hJH7XoU|1DHLb)BHJj7nSLO|n7oN>2MDNwBxN_qT{@%D9k=(*p_4m)= z+au?YP`H8A)&1`8DV0&X9WAY`cs@6Ws_IITMlG1+<V9yT?=HvXi_HM3?Vp2#SW{FK zJG70!Zuj+JZE-QSY~Avzn`t&yn()O}dvWH_S!AwG$Ef@mA6rZR7x>mtCrK66s+~Jf zQo8Ox`)Av}$HS3_D!i|A0N?z0e34U8L#elGhlkY<M04w}xZZOES(%wgOEbYDSui>2 zM9<A$q<&yT+4c=+Z|KA`CGc)gDm6CS!Zodv+wH>m*f{#{4XF2aRadFbxyMQKFmEwI za9ni%accZ^wK8X$aqz@}H`-t|-13Ymi=bo<U4E!2hz{?JR~--IEt5_sT^t%-5Ntlh w-_+gRCGosKrf?pdhW<&IcyJv=Z7&2E0D<2%b_?Ug6#xJL07*qoM6N<$g6yT|ga7~l literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_h.png new file mode 100644 index 0000000000000000000000000000000000000000..4fdd99e3ba0815a92c6ff1a154f15895fc9a972c GIT binary patch literal 1263 zcmV<L1Q7d)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$kx4{BRCwCFmfLUJR2;{T9Vc<(HgS@s zxhzSOpjOeguA@V=5`$LaVW=+|;$aaWUJ-u+e*%vPB%ZiQJgf)=6$8<20t(9-TDG<t z>t#^INNH=gPV2<EIEj4@K2F<*&MAGMwe*!vj*owz^Sk7C4zakf5Ci~Zbg|&{A4%6u z3IhXy0NWD?oH|hy1Xd7)3<VKnJw6{mntJL`%-Q0%SPCHkG))8O3}Op)VV({}074A_ zf;@dFf&lX<1R;Q$jwj)XKAf7GO2c}E9SHj$gx$^}L0XW7cC@VSo*La7sUHi42w)xs zF;w>yo`Ctoty^hW5~E6^>07KT!DR=tJuuM!Yro&SsT6qzi%DE45|kBn217vVY@q3b z|I+t5P?f~!=<v{&q{X5P+Lfh<TtIyGc4qFouig@+GD}!(s=WTlcbpx}YkR5}5^;=& z)6>)GMp=qpy?p*_8;M{~v!oEuTq-;}GWMe^%k;`zc69yW{A>B%=xQq2_lJf5hq3!3 z)uVRMzNZw9v!<$mAk49M?%Yk+YPH}Csl>NVe|WDrd;4;Y4T-|~lM&)k_6lCpml#;G zjlKNwa`u-SYtHV{5Jmn;c|%V_jiN<*c;S9{b#ISqb~ZPPf#c)jpW6wjsWsV(Ken>| z4W=Zpk%`QFJpJZha{kE>6YUpWv~8m<mqS`pv6|U4!i%90X+}js6SZ0`+T-!8Ih_v0 zm;(gDyLGv`d40!JS15Y!{SUu+k?{v?_&sB{N^-3pgB|P{9Gz!)s%qwo+F3yDF=cB3 z%C16HrKp-jOZIM?{CNI6_jjJ25gfrLpAYK)KNaXj#+Pb-Gkch0c&b6{sb(5p>=hOs z14mb*xhpXCNRo5!<hvx=L%N5@mvFeVGjrr`_gCM*?^rvvQLHz8ruHyL7YX-sio3;8 zSuB*IG#YL^CfVK!Y&9&Dr?*sN0~J=;1BJ!hm4hPr`R;7j4)Tt=o%BYr+KidmGdM;e zq4r-!)8JEbxm|pt(O~fy=hM;~+gpCTJhoC0)2%>ag=GEW0kqL*g3mts;zBqzOxKip z+{_*{q6cvsY?m=GY554Y=b-)pFYv*&wY4-<5j~JhO!^(<uFu6hufL8TtiSZ!5TCvG zJDvkvo{Ps~G0x29VH@n!pz*V?@Nj%b6vw<?Z!VQe%{Uzn1<zYtzKX}^QLexKs`Tjx zp9Ew5QCisD0)B7LOeAuSGqc-n4o1AqTO}@{dc7XU(x1s>GP&B`Ond$I_D=4dci&zu zmrEl(0e|rEgJpIi^U8Y;eN&xmVqUu=Y381W!(mR`5p#;71UZhoQ7CLkP6y37os8i1 zdN`?64hsC%HJi=O4-OAX_kX&_T1lIpPi^(MlV<-s?R>;s9K1MvFy7qUj927JQ17>W zq9~5wxp*Qmi8t5f?7mj3R@qidRVcFa*?f4OB6PBQLnsvDKnURC$Q?B#SIg5cO<X<v z-4WW(>G7Wa|F?&Oo5LAG4NuV-durqqUw|u>3d=CesROH3<A{}TPH#$*l+kr&H~fDE Z7yv&$p709D@Ra}n002ovPDHLkV1h#bVhR8N literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/select-bounds_n.png new file mode 100644 index 0000000000000000000000000000000000000000..8a322ce89ad0ddb4e9881f0436d5ac981d398203 GIT binary patch literal 950 zcmV;n14;aeP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#SV=@dRCwC#R$E9^K^Q*gc=o2^T9E5Y ztF4-ADP1I^iz@^@`5<&rq?eW?D2cQ?MAS=Sre;DtR1ie?qOl5sg`pJQST!S!yy0a_ zaHWOSG<H|dIeineaaePAUAsKgz{fxH&+PxreDlwLmX#!l`O}SK{zCgt`db_CZ!$4{ z(=uPtr7MqN;(-UiFdzV50Y_C?sl#8q(TVZzmi3ptzND0JFMhL`WmzZe0LC|gUW_-8 zH6D*=_<mL8l0VX*<@FK*M=4<qECX18W{eGhTd7nglf$g}PGyA05LkqrgYAQyhitaV zt)eJ4f%qx>*WuTmy?8m+A2Ams;da2hxagTg@N9CI9LJr+oJFsXdjg*gvL1wgH;@hD z7}&6th`9iSMT8JR4v>UFTSxQ23ad{`OMPfE?SBr^eB9#)*dAaf!$?syNx7d92Rdmq z&~W$mt$9%t6oMd%G+X;{o$3^0a(ZoT-36Ua7fFLp0(+2~oQyQ2J0-z*5V~OJ0VPhO zMc{}}Q&Tw;W8>PH8GG7|l2UR7Zm8gd^hs1+ZY{-uMY1@<!^793-wMPcIG<=R)R4)p zj!u4IVWAH4cTkCw=w?Hb*9pXtPH#`Q9l?|!`-tH*c`m~vqh{~IZmTuMU@#P7{tb~> zrqZfU`W8V*^<k{-ZGy|?8Uxui_#OckjYc!#UFdeXn1=fLoQ#ZJM2$>9qNHr7@=Arp z=eMa;s@Ld~r*Q<NC0cFNsC<zz#rVFzf1rT$u|AW==RzXIW523UD9C-iLKdeZn;Wak z%Z4H&BkbDf=-K0DGbx8mLPA0m)FAg?e(z32*Gd`82h1Rr&4S57pnMQDP`8pyHNR}F z&dtlM$EnZ6#OhK92ZuJ=r>BjLmS;{%Cd%&iN0AR$@KGSDux1CyO>o)JZ-ae@YS_xM zTq^t(8278y>bS1XPCYhZK3#C43BE;A{{iyUmgEIL;7L(mi(yoNrm*;&DJ+cNf_JzN z2RQ{o>_IvVx!Yf~v{X7ApX2A|zRc1SdI^PP>IL~}<0-LslOlyg!O4mNPkrPY>y}9z z{7GpO=&m^#<d0vXa!NiMlUDuDLH=YYu^`KO*5@4xGII7;koq<;fGYl9#6R<A`<nm* Y0H@G>>8)O6t^fc407*qoM6N<$g1QyKdH?_b literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_d.png new file mode 100644 index 0000000000000000000000000000000000000000..94016aa370353736f4f1aa122e87395cf75d3964 GIT binary patch literal 1215 zcmV;w1VH<VP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$VM#<mRCwC7R$FLXMHv3hnVGYf?55dV znwx2M-PpCZX;MWRSW~DdR&BvhiuA=|sUiVI#0RMlmf9Buu@oCct$8zQ)f5}N&}a~i z_e$EBN(r=S5_4^`Nt*1QoIMw3W}851OU`-7z;HM-=gjwi|Nqbbe@aVBOFe*gKBHKL zp(qLnA)u5(W7+`ISQ8b10iPC)$7>LYM4+lFR%=X8Perdzr`iRm1l&|xi*SCvv}m=4 zX`1Lfb0#Wj35Kf;D26znF$ReuT<R7u%dNFc3zltxs04%(Fb~FRJ|8?&GYkVCz5fAv z&h?<at{(fJeZ~{0_||jQ=(;WePqenmr+D|2&D^W5L4{k>nG8~?*&EiyyI2?dZij17 z5yx*jI`G~*$8J~`?_yo-yH2Q;J{Y)g7NeK@asJmXeDU@7c<qg&NItw%?g<z7{`m_| z9BM}G=$8l;SArE)faDdyUs{9UT}khtPbb&G&@<2o;G+t{*+sOzeFWuqRN>AIb-2>s zhm#*3MIu%Sl?7m3n}C@b=CLr4DXs>KZH0QhBDr?&{ud9TJYPdeNJD|2Ar=W@{L(*Y zd-pB5|H<(;(f@lVrUv?O`p2{A?74!!hICA7We7g>67-DDvpoyTvfMLm-18jJdkXzS z6Sz1y0o}Aw5Ty9-R2%fHh3;=pB3cx};MFuNJ_AEzC<th{_-`Mce6Sg7v^-vW0o$P< zZkaAlUPe4|Hy+t`Kc3pvh~#|<S-FCFdFW{W6n<Zj7YB;6un#pwK@^94Fl-0osd;pL zd<>4`<gSQ7cFWXG&wv(G!ZFe)3`Vf6Xe}P7PvF9(5p@4?9$_Cv*0Pb#+DN6dNE;^i z1`yk@8TankyWC|IFQ9?yE!=Wo{O8Y5SXu$w(BYUFgeDgdU<9d578!n37CEqCI*9PN zSFK^#zGolG;~R5^+qPuv>B@xGZRVX}FY+Q`RMj@1G`5L@pX0UH!Q_Ggaa>`5A(GEH zYlj<Qg9gG7+z6qvK6_%i<eswXDx}ATG5A|2#(IC^*=WP$pk+LUL{(sNu(?H38X{)- zJq<FJj5h<y=uJI(MNsiWN%eZn%>D;{Hm^Je&qiS0uprK|vW_&s<}S4OtWMNj|4)&? z<QAg9ReM-};5dfc4`Z&o4Yr6A2QBnd4A1M5rMQNXwH4U0?|E!&+_{`Qs-oUfiMZ}b zrMnKopV*G+ua3Z;8sy32U}4b#)%-|4z8jAocqPYBfDzGS*-e$mGpKM=IKCBH^XHg4 zeHiB7ZAffs!0s1bM}Bcxj+u-jR|guCpp@PQln{4~MjptMLL6T5#38)=*sHRgi7O%4 zElH8wq_<_6lP$!3*B8K=#*>2^D*hu}MTokp5_K6+cw{9-NrfxC4#7g|S?)fYUmry> zC9dTw^=ZC=)J!TW<e?Wes4g+`pt43sMr4cTOtq%jxohoRT|Yz_)3^#}k^!Ksh6)|v dv*~{U1_0TBocyO7eVYIP002ovPDHLkV1k38JhcD- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_h.png new file mode 100644 index 0000000000000000000000000000000000000000..4448d33939768ab19b8032bb5d7ce695c7b108a7 GIT binary patch literal 1400 zcmV-;1&8{HP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%8c9S!RCwCFR$XrtWfVU1J^Rt!X?LYd z0n<W-@+BY;BB6i~6b%=~U~Hl!@Dsd&AD|aPf>$Oc5RDp$m~cZ08o-!(gTx{&Xbr8c zQfa%pLc85|cb%P?_kBHQrdzNr?RrrsIqB|n-}9X3oadbLO5I&uDFA3gvtaR`akL96 z0oK>oTgm$RMMr#;&05)PwoT=NTeXP<2<9z1DB+|tX-gFXfH4N5ibJmJN{;Kwp65xb zstR4xxTa~MT2PKgcnB(ha|R&E3mz(#O3JB|r&bRP^ru#>YR()#al9`Ui&dQ>%qkF` zEI0?w2$(Nux$(S4fS20aTj?)-ujbseAr9fG!3%~qPg*gZwp~_+heyH`Dfnp;K;NU$ zsB4-A(=gptu_aK%#t7KFKXl4v`TV(a%kFk}FMsm=r)ST!JimGK(2*nW3@MVtssSZD zpD9C==J>%cw{Kd1{@!oh>$$t~#*bZV-amOdx$C9<e(~EcUv9nIb;~_-`aP}pnq!-0 zB%_((jB14P;o2pcMDw=$vjL}=axQ>*9!S!^Vc$J=_=MohRAmV?Rfa@FfiKQ}1P#j? zVCCwyaDSi=E}s8PYkYPYXhz5c;WgTYu||<+P#phjFuG#n=j2mSl<Bkwq7|SVJ9=zy z+_L-^Pnu^>j*e%CHiQfnbXA3rp~1*V8gjXD*uDQ1`1r#Qpy$>NtWOz+(vM(tq6j4i zKo3Xr&urNDrN<>+ssA(}T5c?dOa=25HB$u#4<Ef}!6f|gU<3w-(kLbd(U1Y(UA+R1 z>%onV>##HtgNI|2kR6=>eIx@BQwKxW%>xY^6)Pzz5fK)OtneqZNO;&X$coLDx1CF; z8k?G6&(7`e`m6h3%cgY@iAKRRLvZbzufZ@3kQD`LYeLYFh(j!5f`{kwg_808m5bXL z<5EEIaXS(Y;i-%hQwB4oC%keh97|xm9nj1u?67KK>&6xs7#xPqpYA{yS)pUF9Tz4` zHk6S)Wg}LX>|guB&K_z*zf{DHJ<cQH1wxuby>e`6H+NmTFf#B~Z6XQGb%0|4(V9t2 zp$dgk85}PFA_kQDWIO_EceHjjG(MN-_|9=$3Hw*7h$&F7pfSjUK9y!SjS^eh+xgWQ zsENg(sbxJhtXPHMz5c-oWJgSD!UjYTkXSnyfTU_RIY%I_D6&6pS!MG0BBkNL0~y2I zBVW{7@r+}aQhnW>FxdOE@0`qvhCs$Z$>HC&>(5n&eo{^A9@05YQyBrz^zGNQG6tXa zSao%o;+U7>mBoM#Y}cIvq-lodAqYGguj}VQse*D6#th%VaG>;xr;p91y>DMiceYOy zviEnxR86RUqxm8PdJtfk;fL$Dzu3{TW#<DdlMq3JiVE_~O`}#MJ>Qeubg)Ao?(NV2 zcJq+z+Dkl-qgwrSSYEzz%@3`wy?NUgpDbZt%G@w?Cd;(IX8R?)VD>IB@G)8uPBjlp z@#UXP{(Ajo#s{{>6H9t_9ekq$Q(7iZq)=Sw4EvYqy3R2Bd@jGimf^vUTj-G`pMeMK z4qQFdyysijaTV-2i5Tgc%5@#r8;VC$HI8frOpVi71biV)&BSURQX}q8L9O86XWX+m zln7Mw*--#A^5*kL|5HKm1#3DpSV|)WU~n3B5~!-eEA<i!5fD?W?LUG}A17v&I#qp7 zqV&=QxKJoqkw|3Gub{<Z(Z6t$dM}LUa&2h;+YSF80R{keoo|Cw7+{?M0000<MNUMn GLSTa1f|Gav literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_i.png b/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_i.png new file mode 100644 index 0000000000000000000000000000000000000000..9f9b1c55be17f39fed3c432e9239dc3c7601dbc8 GIT binary patch literal 916 zcmV;F18e+=P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Hc3Q5RCwClR!dLRP!vAxyxLC7JW#|J z0=j?&#HCUE4X!l0ab+}-xX>SATp=1?5h2luiTVpLN<s|Ag~ALB4~Yr|X9h4DW?-hB z>9oC`TLwZVlzC*-#G9PlrnkLc&-Z=j^fH7H_}hbpe=wY-9yr$AQubzF<@wrx77b?G zyr~PRd8Wmnf++fkX$lO>DoQ$?ss^-DO<4*Uot|o>bG4peE1;F#ff;AvKJ!QD^-7W# z%gc)yZrwww{S3ooGVAN<iZrsTQ|j9Gj!rjTdN8Dw<t2a5ox5Du$rD>yO-xRiy|;VW zQ(dPG1Xm10R}I6+nI=(tZrxnBbGB`!2zVSfa-d;=#RvrJVW7Vs4(xA))|O^i{2qgs zFJ7^S4mW`y2oCyT2uK6+`8)+o+nLN(Q>V)%^MU|8&jZJC;Pd-oL6#vDdTy;heHMc7 z*eEP6$nbt-1ftOih{t1)&1SV9KUU;|&P+v`M#n~z-PdnGEEa=AJPw9#Sm1A74}xy! zFg!E_f%<w#rBaYgCc%+#0LOFSz@aJ%TS&;<sg`X)?>+7lG);r1rX#SnmV)`YdH6gt z4fNjI!9ft~>MYttuZXNTLDwm*8fdv3rFsv>vR(7A1+9}LUf<9MHRB-|3_?d|C&*}s z@kj*R9uJs>byZa%ujRocCZG@2(44DP)#=3x=YM4~nQs{2^Z6ivng#;_kR%CQE*E4G zNWnD<h&qDH?E*hO)6lT*CrYDLU8mQsTuz=jd(HxCYioz2tu2_D4kp&BqA1ogRA*{7 z>RL)M!)nGhT1i#S!R>adxm=Eqgu^g{n$pcp!vs+j(VC1!DXv=|LgT}`VRbjavOqbG zSAY<nZca=>H3JO|LuU#?I}~j&CI*Xo7QIfY>b;@<J{k2|@}O~ONq9UPbW#^n6XkUs zOUO_Wa4z!cqg)EG?1n$^=;0EU$1KCJDwd&Dvy^5G+Wr!g;ju3Wm@cJdr464vd2m02 z^X#Ro-3O8Cy?DK9=z3y$JW^~NQVMw2hHvwWD~?S-b2qTE8oph|h1mSRk{z<F`Edn` qGAn7CT7p(}AS<cqe|OmaEx-T=$(r0`jFV3Q0000<MNUMnLSTaMvYgBS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/selection-to-prefab_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d30c67f4f299e34c52ec1a0bd8674c0d2892b4d9 GIT binary patch literal 974 zcmV;<12O!GP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#a7jc#RCwCtR!dJ4Q4~HiW!h<J3oQvg zBFMu9ps;db5VmS!qALw1#*I-I1|$3k<3>OuVnj%^YQ&{+0V<Ig)EIe}mx+;rtqLoi zeN1QW^_+T1CWOYe0bO{LliRtkujl)|duAlgIsEM=!#`+Qe_F7ws5s@rz655r26RKS z2zID9Fqr7KHq#AFaX6hvIAax(EKi14S6^lVny#jH0`yN#T%n$xx6?>OrwzlqVcaXm zE!zPqXS-Ynk=kSCL3*a~bQzP9-BWUHd1X}r{-mg#|M}x+)6H9@HPwfd^)+MT6VaCD z7WHsV?Xo0E<A$M67={sKj88OOzxF<vvy&PL0jKdGHyQ?H439StIy*XG_pY5#Qd|Uc z--5jB=`(dtK_NIC4mWP8MnDRP$KxOH`6C7w4o{8VEJe9$cR0Xqw}YyxkdvDWvw;Bk ze2)|J4<7oY;h{m8n+?Fb{(kWL#~>66LNpo;EiBB};}TUQC8vC4{6mA?mm3-(7!1Pq zPzVg&NPxfU=?2}<p|7_WJb8Jrw6p|^i;H0SZUNP<g2iHSN5WwOjEGE)ltFLbYu~SF z8Wa}ph2`ZXnE5&b(^Hc`W3Rir!RdA<Xm@5ik(Dgab)r=REf#a(_Oo<^pq8f$>UOyf z=)d(r&FtXydZDta3Ib?|(UB3b+HAnYd?XTqxE2S-8K4i|pgCWNWB6vENi{uprmkyk zZS6b;<mBXl2Q~G2Jm7M<Kv5KkB2XCT2oQAy#i~FquF22e@dTv_na-EeDueZxF7})} zbvgl5R#pzBCB>MT4vaAfhr@|Aq%)a~TuT&7vbG|!(}kWq-P+5oR_kOe7ONN;9)>B@ zln!SG1E<r8DM$%QaX#@!C_cRH`z*A~98^{9e&D=<4(E)MW}u;As3#Ckfdz(P$f&2- z$4rra8T56uH=th4HZ%@h37hRVo#bLNQC!#Y3K^df@WRNzr$&>3Hg#^wwYT$bTN7T7 z6OtrP<2_8=S)v(Z1v9=eK0H*1fCDBi<H@p2r{J^ucUng~+FB79d_*<2=Z3ByN2fj# zYe>HsMp*PlMg@Q4TJZ{_f^QYQMPw8gp_#rYAnk&`nW+SI>&~>9KsD3cA3ra%GI;Sb wlTl<-eNFjWHWN@V0U4QWmj0WM?Oy^60J&hNy+0{M*8l(j07*qoM6N<$f`$RIeE<Le literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e149a7e9985699611e385c1f86a3c8167acbad23 GIT binary patch literal 387 zcmV-}0et?6P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB}qg<RCwC#mOpC4Fc`(3{(KT08t@GS z=~Nu(1wzl@i`1FYYov>>;R7Tzt38MAp3<eY`)5iqxRRh#8=92L?cfJO$O8TJzGuY8 zvMjp*klSGpK#Va6At0rMB2|&1L^|M!R%SdtLli~eoFkO#`q4bpP41u`7*8fR9gX~r zLaA+Atd>jTZ8?XbfllsxB7%pyl7<F;NNEj@o<-K&!O&ScJ8Hf6Z?;wK-KmfBFQCjm z4&CK+it{-Bw0S+BW4C+z(ZP$G#h2g9vIHFTZnNpWQ+kyiwce+qD7qojA=4q#A=Cep z>A~gwE&1{a(Z5+Ph3p?{`_TTOTIs5JYJ7VbTKC(nn<`04CC@Lf`Fee8h?MQZ2|^0@ hZiL`gb|mit3;=!h1QpXI<S_sM002ovPDHLkV1jI8u(ALE literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5a3f34518f29c3661d04315f3a7f49fd421556cb GIT binary patch literal 592 zcmV-W0<ZmvP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz@<~KNRCwCFmcLKJKorN{kq#y-HJH?a zpeFbqiNGKlO(YVT8KIl}12}>kI5Odci>|tBq7V{n;?My$?w<Fk5veV0iS0|?@a4#T z?)|veW6w`d+W?T0u0r@`N^dI!)HF@q(zMWtpN63thLKYQ3UxD)0OYgKp~M+~j8z2@ zfO8HQL2iX~;z=li5FCKT!iS;&;>A!15S+lm)$3RFY7p(}l0!Vj`hiPdeahKv7D&5B zOHj!M?nRVl|Bsh;Gvcww$A?-vy${T`*l1i=WP@$2Q@e8cQg;RdyuQ9rdS#?CHH<A| zi^cQ$uLD#|r3>A!rrY2Moi{DY^a?Az%qzXNjl8s5q{NfMfzLY%eKbH2?S5@Iu={?$ zwzInjF<Yq3W~(AMTC1jKXQ#R|5Mc2B1_~DxDF=IU3WcMenzmXu)svHB-LGDiYT{Xq zL^WmJ(<F#?ZY20_B&10}V~}|?k_e>TsKjG;y<RPq+WE8I>2%hvX+EFV-PVS~!D0;B zEt5H@FAi{9&1MgEzuIZPr7=jg5Qsin9RulAy<s(ykBj{u!@-JF&fadfTT3RB5S5Ka zBN~I&hczm*zNd)3N7(<o9?*(3O6WXNj5t!_(Yx22OeShP9uFNjolfTuZmMb9G|im! e$X@tg0t^6ILCA!A`3@HV0000<MNUMnLSTXv7!7Cu literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-grid_n.png new file mode 100644 index 0000000000000000000000000000000000000000..618f71d523d94f83467b1c07acef4e40b4c7589b GIT binary patch literal 310 zcmV-60m=S}P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy*hxe|RCwBA{Qv(y1HAzg!vKI$OKA#N za7m~rD8kvXNh$vq&zr+YEFT@*d2pXNOL2q>$l(7(@c&UOc$tBi9ZD0!#|ap5<<rKd z)^|WkkeoO}4{G$_#c3%tPC+5T42-j1K!bRIY*rxt4dj2p#|Pp+&>;Q`qDhTYMkF<? zXhDaM&jhoOq|^%q|DmpCfck(~8s^jgqj5SKr=xK?%;FTeYJ&n$Va^7`zkv8B5qz9+ zN=hyB4=u<D@ky`5v4Fp5K}LvA6-)n-$S1u%9c>L#0{{dV0Jw(9^#uNm{Qv*}07*qo IM6N<$g46GPcmMzZ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_d.png new file mode 100644 index 0000000000000000000000000000000000000000..56d08e5d308863be6f2785593d6c05b567e15156 GIT binary patch literal 1154 zcmV-|1bzF7P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$BuPX;RCwCFmRoGobri?H?f=pXTi0fk z?!u(mWWZz$-CPt�L{zh*^xpDB(p-G(0RpdC{35i5fL~K;w8N3XeFz2f<Fr#so*X zxF|3gnqeGmx8B!wz5QFy`E519v}uQylbme-e||lmbN~P3p`oFz0ES7OsDkKpI!KZP zS(c$H>Wrc)eKG@02G!Zt_9$#N8}xcTswKf-!08W$h8Ze=n>#yEYq1EAswLTM79%c~ zQ-sA4RvXB|_7z2efO1N!4J?RiFhGxoED8np(4jicYNdw<4<5#)i&qg2MPapB@btEB zynV3$!D?H{p+g5g#>7w8;c%=*W8)gc;|cuW8pqVFJJ_(XjohbNsVX|UHlcTW4=i=H z{|)-vjoTO>bECDjg@#;@oTiC^w^+$Ab>g;N&tu*CR`_Q<xH@_XcaNUHi+lS7xYW#- zt?A9lDHseYuNG*zJTe4bScqeDPZ!!c+A-&yr}@jk+F-|y-7g?7OPILern5QZNFNBw zlF#R%Wi_G6#T05N3ft;>VU*SyGW0&9<&h<A@7anwl=e&}QxaSj+R?QU$z%$P$s`%k z#8$Cb>yRd+sZ<({wT;l&DFWsw6ZUmZ#A0z#26mDiUNLmTW9``c`fm7q0YoD)0g8kp zq*3&2e-bq|E5URK1wtSul8MA}b|9S06@gaNy?fqx8NOK`&VDy4o@22Dp4s^v)^FKF z#&rVr%e7w#9EROuM)M<0BEn)(Rurl4o)2%n(ht}7<1m?wsI6T^O)KN{u@g9T>?Fd$ zuxPT5$2&1~Z4$?ie1+*-zk`sX%U29#;5%;(VD>K`taUXouQJi@RADxoAW;ybr_bPv z_dmt;b7T1VtP4?(SB#ST@+9`{*^hwFzjEkD?+wB~AA-qj6t&T~IY_x9B^nJAE${H0 zAKxDT2Hm||@ak(XArW6hgTpTB|8D;wcxS!hHx)rIk6*#f8`Cfv4b+LbqRKVn84d;_ zG_P~mvGZB9tZhJmQf{$Y#QP6uo2Ap4`@kikXHL09G#phS!eUy8dTit({jXu*lLPc6 zkq`+*L^=Mv>%r$Aeks6(nJo#OzWoQSF`XFja?0mCRDYh+dV4+g59||J;8e@hbQeY^ zkV>ZnnBSL#dhX3H&6<`gt9;Jq4IsblME6rq;K<j5Xlh=w1R{I<m$wv0EAtXBB^f%n z65(d!TC+5@G~w{k&*1*?3L2Un!Xe&f0xC<z_rr+$q*#CPL|k}T>5k6jml}+(okr^J zUfNpebXwG3zZ{^<3%lH%l|Gdf3!g76g#uX8FE!*pcVL5Rm<feLP7eLwmou4X(P%39 zi7Gny;*uxb_snC(Has2=KhIF4YF{L}Fns>}Ij5qibPHF+2#D*z|1UG7eSZru0L#5l UK60*zod5s;07*qoM6N<$f}5Zw^8f$< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_h.png new file mode 100644 index 0000000000000000000000000000000000000000..39df58c9de0bef06901079aae319f2d35348a2c9 GIT binary patch literal 1381 zcmV-r1)BPaP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%2T4RhRCwCFmQ74tRTRhXeeW?dFyFv1 z11;0Z@L{!7ia@LjrCl_dn21emrNO3&3zvSxg(kW&aiJScOq!S&Yg7!_;KGHAE{xb1 z8&fNg)_{ZoIv~RUGH;mg_wKvTbKcC9VVoh<gx=(Y%;moGyXTz$J@-melaoF|$Pk!| z9Q=^M#v}xKy<S(N*L&c^&1lpWjYfwgu5*{Wp@HZcJ8&p+B5M(sr0axennv^okXsIh zJb>zibBz!x96VI#1bLFqIpG@M1%B{Qt`p=*9H9^?--G$_2cMi;S_*e7xtz;nHp_v4 zfBKELU!C}WV1NP}BGsxc`tbdao?eTro%A#|606lpQmK?{abaoTt=HcTc)gw_LMf3% zkp(+~D^CtQv0``F_f#bEj7T*wT4BMR+q15f@XAU55kC<H!4Mn;2skD-r}M~}!DqMp z!9ZetHBxh9;+Mea$av$qq35R^PKS!?{tL`UDqx;9#tet$HgjjjS7S068^Xm5=90-2 zIo5rg1UuSEY)f{lsw%oX9^sjDX9Y`bt>gMnzqTrhLRlrBFo!%rM<_;H0g6gPjTe9C zvRPtt*odpXK3^M2)KsQYRbv3r=&{}&8os;SoX%uLMO7&)_X*!1PvE*{D2i9G&Z8Y` zxm=E90fryp?(I$|fk+6>yFCqYm2s-E{Cc0i73VFgs!%1T2&&3xsXv1QJR?%3RfD$4 zJ$*gjZ)|RoL?S_Q0E^3Uvc0`cf)BNa>g@I$rfXY{TQG`rDs2b_qgDW%KNUOvkb^u) z;}|J~UPWy$To{~^W3qcOwD=VMM#qo#KNj(~1=fw-Ljgi_q2^>FWu=_Wx!tZ+JPnNK z2DIES@+4lUX(jb;Zv12U{L`;qOeK>6i^W1jQH0$x^7FMH4ao7{6Z2MwJrigTuKsZK zyS{K}p}*x&^Y5@`rbaMvOgoimnR<01UcC6yDW}6>OrUUzwz0lx`RwwQORAzCw%M!% zRV+#(h)06Ufs^HhMRRC&&chTXIy*hB+?`)=)>-Q;kouNezfE?wbp~hbcAJ9d_?}+~ zPL9j@nj)QJT}y1_%IF{feU@4a6n{s-m{NqB&1Mqcib*#nu6G=6ZJF)qKXEOS&X6XL z2kYAO%@?D?kw~<Tp_qz(jW&qtz1IeJ?2nsMKB(hSbj+(L8wF#6=s~6Rv8^q8@8dm- zwRN@OSWG4k$ehMB@93A`o(JU%Dl3XgD>Mh48<CXSt(%kGSU;m%F|Dc@QNz0Vzd~Km zB<R`m&yA~$#bFTG0E*5(HZp!Xn^gpj7dZgUUv0aih;79JRG?L(EHzx0N~W41_0;Kh zWKW+NxC&*-VydwWtIMl>nM_8?K*QjLhGV3+yc59(KI3vP^yR$_*JU$V4>S%6(F$GN z53dcseBrX$Vh#h`P;EM$mLSbWm&)HVg*&c%Fjc*;D+^j4Gz3P?uxXE{A$94M;jeE` z{dvgUSf9XV0gXj4nQAETbg5X!((&dyue}HDOiUz_rfe=J?FWkKg@#V?FB~flBGbCM zANgBRR1q^E2vpY#EI4%e+LRBtnsg8eH=9iRezD<t{I1|n2lX?YgALH4PvjXLsS*_1 z8+0KR{zvx<g>Z~}snj3LaViMAo-CL;Ihjnl@KvzO0qjbUMDQWr_mf1|E59w1sg&{H n#+SpG9E%NsJ+K%4zXA*ZniD>%hwa8^00000NkvXXu0mjfyB>}u literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/show-preview_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4571767ae15de97b851b3e87ae4f92acd19cae85 GIT binary patch literal 967 zcmV;&133JNP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#X-PyuRCwC#R!dJ)Q4pSUA1y^%ihxn5 zQXWPWNE%FBph1WTi7Pio<A(TH;Lf;qfxp15E5*IW6~^)iLhzAjgv7WM5KyB~OIzrD z-{VYsOR6cY#aCiH&E(|XbMAce&3toC8Rs1S^bp`LjQ^nj9;lS-WmB;Jo9E6GJ%c#) zqXp3x(C$u+j}2}Yl;vufY)}Ne>2kTRR#sL*>5fuJZX{uKbp;H=m;}ZqA?-j#drpSI zs6PdYfI&eJ=L35KAPDJ7=(-N7qJrP=hpz6f+f9uPAKoo4@!{cze0h24lPCy76C+^- zxAN`QG$hM1F#K_wgJoGMrY&u)5Uj6<csyROX_{QKXSa0u(gi6{8>q2u-r@LV+ZGaK zg22c$O@}hC7b^S}_})hLqHY+bh^ZD#(-b<6c96ezG&vt|A<$V}*Fe=&uxuMF(*&Q- z2fCrB0;sCoxrD(mTw}-(ni?DF8$X)pSn;4JiUkzQ#mH&|*4GoDVE~v?NW>Em2-Jql z%F5z41`}J0mD|XCBe}7um3*L5q4Z9TjShA8oNP$M<Jaj~NhYEBVDqbBUELd=Rw198 zpFgZ9>aJ)sI?Nb*;efHC`ZTLuC{+&xCUm*nGSx6u+h<RnwxfcpTUri1D);%K!C>9X zk?`oi+}ztMj0psGKc9NHY#9e|0(&&%@pvHZEA5Kf28Bayqt(>b-g>{htgNiAoX2$U zLp4(mzs8+?n5EA+TgBA$jwDGTcZnOQUKs^jwgsXjg3BdCH2TFoJ3DhK5{XbYuBnOw zRaMnc;&!*;_u=C`y;Zp|ge}q<LIIsdme`+mPNN~qE}Hf;1~rdI!{er9O|Pv*!Rz&b zEJ<w~_`R(5Daf>662(+6ZZgbav`+i5UCyP{%K2GEQ5H$z9#1JC`(w!Vh6BtBfp#%G z_AJXTXtWO*wURGvu^0lK(KI!tD9KbAFt7n!E6E1l3N(bfQ%njO?L%vEe2JLm8C38A z!!)N26daa`fZS-V4BnP4g9_Zv-Ao4pOpcAL;MCjQ+jk_9NPKkGh^a<u&UedDWL_uA zA06a0L6?yO<Z`|za>RmeA0yx4K+%{*vNf{~1v|sc34mvMHXkT$NEZ2*#{TXw;0{BU p^_&;fcI;OSUho+F-Q%kO0{~-Q<0Wh|X0-qS002ovPDHLkV1f~1z#9Mn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_d.png new file mode 100644 index 0000000000000000000000000000000000000000..8c8e2c69c3af3b417103ec4b983cd1a850583b4e GIT binary patch literal 1358 zcmV-U1+n^xP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$@JU2LRCwC7mR)R9Wf;f*?fE|KI{L9M zTA;2n99!AqCP*-H0TI!NOoJCBhD2{PF<$9~x0*=wibRD3FBGF*sf#8k5s)ZBnJJD9 zvEXLu2I-({d-}0<ZBM)Tyf0>mveGT#Nlwn!d!FBUzF*71g9m#74ASApJrIk<0znWU ziXvo5j!LpLATpq)$o@d!emI>@SglsvD@mo2{zS?cWKa(54hGTSaPU7`ikRgxvj_9@ z^RNomn%`F{6`VMJ+|SKo7A^<Klo&|^<x+{Rs~|Gw=H}@~OgdNpx1Q;jBnh0tT)I?X z+AvU@Eg_T2!X%)stHe~HC@hY^Ty>6VCf%0XzLRNzxSZG|u4iyL9EK=JsFHF?5(!d; znan`5sc^Yn3%S88|2J47<CX-y7BO&S|4D3q>OQ>v*1HSOpWL+_qvtP@l`^`54`9!} z0lHTBy-bJID&5Lv=GA&&MxuZ(5B-AB_haaOWDPuCnLls#xDihzarB!L3v0*6Ch+r_ z5Oh_+J0HAB$z_2kNw>%LZouz8AHlIBA(RRx6kWo=OZ^xbI>+CwT-m~*_68lhcJF|Z zO5^lTLzupi<nIf!vrsgJtev~96z>ZC>q3kJnQYwAf$Vew7s8kL{no8paDMa;1Ok4% z@ajHfGdWxek7FiR_$S%Y&~y!oszR2R0L>)waCkI+{lxP@eD%p7Qt{Ny_y{)>B%96Q z^ocVlmC7g<iwobfd#WQ!rn&mv8~QQIp>KS+he{)lp<hFYPF{lmd0~x8vAYtvio@@H zj9Dt7w2?()V*@f|HG5|j1>QL7hMqkZ#-^Q}Xmn|ajD;~eG)nzbN7Y*4?++X}ys&mE zHhs%G1Y}|-_?@ICbn;Rh+n?*jMC1xC4u?_9o7BBrh)>7Szp)3NCO77-)PYPBGnpCW zvRO(P0iE6J@aSV(pla$J_15g}*~Bb-0XxPoN06T>!{u^Q_bHLvM6`KZ(duh~&8DMV zC{cGRQYW&askI4L&k1Ou%$8+|PniD>k(y9N6ES@DPvnd|8X6r`8uaIuc{p88G`D)l z)e<UZg(nIdVP0<=`ZjGqa<T+n)A^yP8nsxVe)qb(HjE8Vp?!m!luuKlm{_^06}nC1 z4L(aQEs~X@Aj0i-qPH(d3f+|LH=r%z+C?p8{W3pOKu2FQCN8F7ci12)BAS{#X!Etg z<#D1+Bdw6ngQgjr?iE<qwHDodT?n6zqvJte%`#jBTIDecZuTOdF5~>^Ni?;%(7tXB zoCIX<G|eiXOez(=Kd=hjJ)MX}N*MbshG$>iymTm2#$4FcwgS6fc?hASQ;3XYd7@Y; zb|%HqLbb;xig%3<>wDHCmsIfS{!@5-UqAhrUK6@#Oj-M4<EpD)!JgMP;_LTAm<Su_ z+wOy?R-xHdK0zAgHhlZhWsDEUIB@6Qp1Tf^nu%m<utOtPKfJ92{tgdLe(^g_el~`@ zVZzZYAseS5piN<IpBJwmdIr9BXI&#w6olFvF;?t0yB(_o3Z8yZL2+*h$s1{0kLIvx zTM+IR$C5VzB1`vrgtm?ZxA8)kSIg+^UCTqT91)qadd2RrLnm;B+8&GNa>c61O4LXj zd}mkJ2ejW=E^sOqW2Y!tDi36`xxrJXe(+0@%v+nl+m_{C?6;$I4E!y?03aI4`s^$N Q+5i9m07*qoM6N<$f)v4nhyVZp literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_h.png new file mode 100644 index 0000000000000000000000000000000000000000..3af52d968f0c5c5c80c01d77d25c8ba1ac1305fa GIT binary patch literal 1597 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam-rot_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a6f3081fd116bb6d8ce709a91c197111102a6bdb GIT binary patch literal 1616 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 O0000<MNUMnLSTXp<QIPc literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_d.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0bb6d3abbdad0a2828ae4f26126ccac4e3b6c5 GIT binary patch literal 1178 zcmV;L1ZDe)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$JV``BRCwC7R!eUiRTTa_?-|>P$BrL~ zLuKj;>Np`m1vZuF2EhWUA`8})_yH{0BC&&2B}5io@ed*)v1&vGRV#vuh>Svt2&r3A z7wp<I9zVz92j|{Y4xwpcTgs6}bLVxwIp=)mJJ+|iwzdLbosJaFz<fR*L{Wq!Nl;`Z zFDvq@!~k_wNu|>lV45ZbLBLsArBY5ktyt>}lmjj<Eg?8H#s6p=^Ew?C4+et)1W`Eg z`);?3jhi=9JS-OBB!J9`RkqM^U0U}*$msV6^dlafd(XYk{L8Woj>1wpE}(5$Xt!Kc zD-JvYGz^U}HK;19Bhc^lnP>9tJMx`EFG!Qr#`8MZ-r9jA%IJ}ES(XT-2d`X(?$;2G zgooT<k^dK1rr;(5E&g4^r=NT=yng@cJIHNqQ<Ms_OBeCswN+ZH{92(y5adHPv#d@6 zS}t1r9Gi)t__T~~zu6c*+x_Dq?%ufvLsRkjSDz8Ne2`@MXl=&@tXdBC|9s5Xb8~SH z8VDM=`oVj!DmHHau!+(?W&XU`YC+Xiinf2G6~_iG5a6#n_xbwDmCM-5{f2Zpg^xbI z2B%uX{heLZYt3h5v!NS0R851TObBX~Y~20+M^uWHgZhXE1aX`iZg1Ry>vqs?w}<bu zYw9B+Q-8AbvfkVL=^pZr_8~$U-q)yh5BC1TjW54Oi(1I0^iNL*QKhKaGeHu$aEuMg zXrKEzhumh4^wU63=<@s5uiqHH^Q2HZtUCcR3lR8mHWKK=y$9I-We4qsN9q+uu~ftx zZ!Kdc8o@vy1@b)9t98^IhX^Agv-m3Be)lpoT|3s_sp?**dhPzcheo}Fa5zHh<5Jor z%q9~^%*WyP8|XA$Ql~a4k$`9-itO@5#EIF8BJ&CJ{~>Z>q*mF+<3|P5tOkP9Q|P*F zRIC=vun9A2a)({7%Zb89STZ?_*WSz`yRraXH*hGi;^?3(%DPi0S&NYK5)sA2+)D`< zew_=vLn&=jl#(bR5;3uIX$jfaGh7BwGU-LcX*F8}(x%1_(4V@nNZ_Nf8JII6icr8l zWuVz;fR->LA^tOqm$FEnOP<P+7l8J7jYR50Muw_wBN`85Dimbm37f{5=k@qx(x~yz zou9`mnG3v*qu8;a%o$Ii7oZ03az&A3krRd0&Z9c|sQ1`Ji6vrKSYE)(q)LJ$Ob*J7 z$rl?}isn(oF-m2NAFP%=ic$|~e1c>%8O*2VM+Q8VNPeoPPN_bizQozuA!&1`0+3Z1 ziP<=k=aU?K9FrI+PBPZ){y>0$)n_|938C-+BCXSz^mAitBv3~8QH-{Z1n%d3$07@b zrzZmBpv>8*7NE2m1l%Rr8#kp#vZpF)-m)#evm5CH+V4yX_@q!^r#wZftX7@c`mI~v srDU1*G?X%t%a-pf_S<<nR{s@X06XE3H5F7eJOBUy07*qoM6N<$f(KJBN&o-= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b7988511c43f275a58a2ccc7c5f9209087ca6f67 GIT binary patch literal 1407 zcmV-_1%UdAP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%AxT6*RCwCFR?BZ2MHrvinSFV^_S#<C zX`D1o;)=Ei6$A(*4&}xTam)b;35g#012}Tv#Gw)w)C1zcq2h0-ib`prqKHS;lD?9( zc{p_(KX&7N&kQr`#-wQ;E>an3;>o;z`}@A%_ss`y{Ca&105He91zvpX@_qp`#^xq! z6P8^ad+r<jejiE*0ZJH<6`5ffAGp8uc->lAU6~W0R>7_SM(J|_#&HZ9E9*+$$|oF* zNeJ>;$_SwlA&DXM><0*(<&|X%Q_A?4CV;`w0h^7c+~4w~^_6BO@WU`?nQ;;)RL^MX zms0{^A`yl$dg>fwe2wD?rhGsF91+y_JowY4t7RFhoE5{0Dzhk#VH8Bjw2ZhmaU_Hg z@&y^h5djF}r$7ZB7G_OBM`XFQ_)z)g>+e6={{QrAr!FroF4tulM@Oe>OCO${yJ2MY zSkqLRF$@YJr?w3$I>*9<{~?5o47$3so)y0py9Ao8P4MUMx6cfPveB8oGylaavW)!C zzx?bweksdlvrmBvJZ@f^`V@{}-_2$lKfJSIi}Cn)H3iL@8SwrG?}1Lc3$FZl9c;N? zy58;eWZh6`!Wm1F^u)Xj2s}(vrla}v?r#sQdyf_;#rVvbcfcQi-3GI>$H2#*egvGJ z3+Dg6FF@11kJp=-OjajYmZ`1}1U3*-3d%+@Z*#4!T)X<qY_Juk0S(dMgyXp2%EhZ7 z3?mQ(!Ek@z`#6pxDT*Sf$nwy<sd`6Y$}fZ$FI^tr=yWD@)iAc_xmdvB!ZJAj)wjU+ zL(uIwAfGoukF!=ZMfi;ChNOPn#Z*l0&s3~k`{C!x!mS7OoLS7VCt2j&x%0z4jkS&8 zSOAJ<=Ce_HHyH@^U#8@6^UmM8lbyeLe?o#vC7;jHMq{-<Q=eL>RBeV7q(&iUxY-T5 z-5$RaCbh}2hi|?6<^!IkC=qwl-Jnc_4A~ddaU3*%^R7)PDisUG#Pi%3N-U~WDoS<C zMsCj~omK~J^*s@c)XW*(+2d1<BNNrWIFBVrg=BYv3Ovln-b~w@U3ux=qg>ce3YKL_ zf!E&*;y@|dMcE#+DME;Nz7N76<Q8YBR4De29j`6C`u6E{uGSO<NfHy;X(y<_!z7JU zKDi6Ez0sEYu5Xm9Rk+z|`(EhLO0^UfN|wn@#GI}R+}@VZ1v9dlWM+19;neFdty+bA zw3jZnQ3c*G4Q@AK^M2n)UO!NCITHk7<S>>1+b*I?t*j!9+_gsywcYNpB#xM2Xt7nw zw_bkZ)Y{}!E#QtkF!a(iC|DAnk+xfs5a{<k#H|95A_s131LlgRV-<5$H?oCJtBoCJ zD<*LQxLBmhSZQ;*ezZ|NQVw?BvtJW<m|JQXM`_SYkZHP-@N5!9K@#)l3F;YLq%tM) z{f^Uh)Fg^^tjWyO@_zl~Y@>c++65AK*9dPi9lSE^nFn0e6w3V+DOw@{$)=$n>HOVf zQ5ey{3lUaPAS*aIF;j0Iof&rlWI{|MJs50f$n0*qp%IlYVl2^KhYN<Vm_Hgw%czos zL0B%A+}iP~BVmc{bsij$Yz9I)!1<b{5oBX3q!)M)n|XsfJWFRzPWO<E*kQuiTS%gE zU<V&WoJ}UH@x;JuA6X`)axN5ehn?Hsr)jxzxIgqvhNzsiP`9Z3=!iaihACrDa_@6g zf;>(cp%nbrHYm&h>$*<QGEMV^SK6-Y0)Z!lbfML1&GG)fRrvo1FaV+LJlgEm^^pJo N002ovPDHLkV1i^SpuGS9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/smooth-cam_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6168a6613068bafa82c66610bad13be4be9f9da0 GIT binary patch literal 1611 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 JPDHLkV1nM+4-x<X literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_d.png new file mode 100644 index 0000000000000000000000000000000000000000..3da4ee311f41215798dd1d896ca0b21f55ffcafa GIT binary patch literal 950 zcmV;n14;aeP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#SV=@dRCwCFmfcHJVI0T5J8yHo<#f8~ zYMXCKre%g;N!jSC5Y#U8Dk2JkF1qVRx~Pc$1nEV|)Eh1EQc%%M3^WoUw5H2V%a_UA zoKtAiu=9J&;d`FJ{K9y4o}E3P^ZP!(-|sxC+S=Lz0B!Qef(S^ZQi11r2!a4fl$@d@ zRtf~vs3l8gW)`$sEjW%tq~!4|Slk|O8$lT`FFzk~@$u}5BBiypHFR}$T3A>VVWfd1 zT3-}JFeq6XY2c<LwHh3bSfU)9qVW7nJ~TBoL#NZ>O6g^!7*g5%_x5+_eD)m6vwn7M zC@{nNum+Y3S9fPKxvCVn;Pt}4yaL;JJ&Fnok$*f112vDK@dcrQirxEXcnZDuA0VZp z1JTLGU2AblBh1OpMqF$R?%lnECBGjZpFTpu;wnt1^|;ag9%U`BU`UYIoU`@yp6hW+ zfK}Dih>we7(UP@voh<>nbzmt)kHH_)FcevFvZ9pDsei91hwcQD%$AMDH9t4YMtyPu z1Dwf7!{?zP_Mdo>V{@?rryRNyNQ%~I*tp>FvQe8Oce3?$=shl^>GiPN?TB(ru(=p9 z3@F*S704n@PiNz#)45T1sV5-FQE|)5FdB^*AGd>-^NHr@H^or7L5FPIswBCC1p<MM z1=7qL89O!4te>8l!3*y?o+PR;5@k>h)g@{}Hg1(#lHhi`+4rx!0T!PNrv5RU%FRVE zxQe=AJKi|vU|$UE-42y@lu3sJfnbo;YI9>Fva&K^AG3uTg)!BDw+#&_zF4w<4^{&F z(9?~+{!h&Iv-x=}+TOl?bawY3HYNrc|1x{(=Pwi=)cF4g@-2E<8ML;vV#eh{y2*s{ zigNj_bS!kwx6x6SSQD}flzjA12p3A(I5INIq9K)CzlL*#=Xc#ld&%H78%r{))fzsO zMA6Zc-@bJ-v~N52xT~uR@`(VYmr4%Y%R@n_1gO7K8N95kXT?v=!ellfQJaW?!9h5j zlWc#hs;jnH9yXM0+q91MmAZ{<&V`{bL(Bnc{`)y_Ae841_a3Jx_S(;I#0mV-98<PI z<#XuKL)DVn>6L4dj)#%ftelXtoPsDxmA)l^TTf57MU+cJl8=bIz|(0a5?p#URsI!V Y04Ov$q5>Lra{vGU07*qoM6N<$f;J7svj6}9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f4346a01ec9e6e574aa0b93f132a12cde5e770 GIT binary patch literal 1249 zcmV<71Rnc|P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$gGod|RCwCFmd$S)MHIlt>$P|7^~ZYc z)K2WkTY?gxZIaT8azIL3BFrrZ4#@#20*Mm`Brcq|0rwp92OttS6eCFMq(zB9LKRVC zMUamJVUs5Iu8HimYkSv@cb0kUq>XGRf`c>CNIP$5-f!NU_uh;xy|xxX2u+dBqwY7A z^f?-U;c%D_g~MGZ9x4j2D9RL#P0R;^L1Y?@u0w&7m9so;ng|(&fy@i=EhZg!1ZpCT z4TNk|_n{_6z@tr!5jF^(!sjk1sw%s%aDUp(x#gQTZmdLOF$fCuTXJ6fb>W8zUDvHZ z<O6~<Ht}9+_|Cg>8x)%ecr*qmu@s-fER{-Z>OpE+)3r#wUguMbi=(k<Y$YyuHlL(^ zoMaJZS=QN{td+%IzWaJQHu&m{FC5j|!5C45jSTW}R#%CI0Cc-ua!?S`PN!2He|vmJ z)2ja8f4cXPm!{YrA&{H+_|utp-v4lxce`|u{qf-5XeWKZqby4ykUI1d0WK{rS-?ah zkz`p{5v;v*sYiuz(2f;9>p*rPENo`-qw$+>$>PfcX^=Va5!<Q{IF_a0XQR=uBKUMK z7K`lC(!*(&%O#JGk0(La)6*k?i?Z1)4`adWF8LS-it*f^2bqI0SmSZh2xOeo(WVnP zz@toj?B13Mj~@L@EOg6v@7_tC$QZ_Hol!ntu%LV>q<9I$Tdy#jo0I<9`CGJCpG82% z*lq1la4$fyX;=}3Te@D;Be!plB~Qzu(Wn)Xxm-?!vBT?EDI3YX#u4lD`F0gq9wI{o zGWgf3)8Y`GWog4`huSH-uH<rjtDIq2xp8#Fz&Gs|th6z7jTjx0%9XPC{dN}3ZD!DB z#nuJ|4)ACLgQdjume6JxMkbNV3xR-oT5hK(y;7}4z+LI0seV4UY^CSJ4?dGNHa6J% zPd0=~xs1Z2|DZPvqb>b_N8{r&wb?o(C{!!+MFM9s*$7$0HjWzn^76`LFcjSW=G*z1 zU{FwD9*F_zi?6=izH;TY^hpJ3QD+4bDTB?FX{ACwVKU1wnhV$0*Wb)!G6}FW6bwDC zYSr-5y}g7aNi0n}RKL%=1*;1ALY%BgcdRl*AMj}KG0;(ml3y;D`AVh2uCA_5YMLf` zIM3Gg*RLmCtm}ED+1uL_^ZC43EER`pwORx=g$<lMtGZcddQt{kn}_3e4-XGVG6x3< z7+)4I&kqc|wACzSFXqQ$Q3>3bFBC=~4&w26s*}FcWf1OD%Z+V2f-*|1gPmU+9+?${ zfZ94wk(J2K&Q79UuSI%$`%;5LgU4rSo_o_Y1F1ul`Te|9uhsljO&8PYwNHpkB~sO8 zpWnC3x!vmi{{AiE`d0#i@OXG&c%J;zJL&0k(;VNY?N>}+U#|omzH7S9lAIOG&&m;E z-7Qi$M6cJIzIyfAEZKy5;<o?Np5LdYdH%b>$uN3<e?Oe(RwpI@r#0`>Gc4`i>*3_( zaDh<EQuBf>wayeCz)%D@j_W#*M7#CihBKWbg?$Q&`$fI*{}o^W*K%XY)Na7n00000 LNkvXXu0mjffZ<(3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-bounds_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4c6d83529216639a1279b5e3629d741611a534c7 GIT binary patch literal 962 zcmV;z13mnSP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#WJyFpRCwC#R$E9EVHDn(opontHr+@h zN^(~+knqw|q_O#sz2rj|s6ZmIq<d<)*=zM;c}dNp9zu#JBLoBKr64nvG7GcxA%eUu zL=Ukpl)Ae!(|7D&n1(fHh3cVxIGj1>|L1)3o&TJ(%W0Zs`O}NH{Dt)&^tUcyzKzvx z&rf#6=`${<Cy+tN*~W`CqvZE)E-X+Lr4<F~o@2rDT^!F>c0YbJXpH$`^y8f7=Br*k zs17g}5`d6>-tV7m4+eu>%+{}fLv&<x+`sn)&#Do0!Sos!-o!_aO;3Nzf=(ChV!tfQ zE=iK!L1#K;rx(vt<P&VR12~@$3xhWch;;4;&;-YEIoB^Ye`>0|P{CXIVOf$b$;rtX zDJdzX?QM77JDp3~$R|mTWq8j4u~9}%ff@nnWmQ%CXs`6pkyJ{XD2jebmMm7Q^#N?( zmaRogWm!_l2gVw!8NoED5s-n>eNw;`1Zua7Z$!~yflfpgo~OAz`FPG|J_TnqhG7~s zR}V-5u3V}gpbap~XO%jJbXy=!n7x7^6mmZnZI3UePXi~ad>#f!D+GzKf=VO@*hK@c z3Wp;-0Ly#X|3ZUUUcVZcG%{SUPboOZ@dWyrfkjxlD8<=y;8yF62_+Jl0}TcOJ%`IH z6NiU}(p6PK7OXrwuQF{4RKGM<T-2v7Fmjjss&9j7r=lopkaj;^HSE6PeI=c++jJSy z;$dn8tbl4DrU>2wy$;|txW}a9r+j0PNaP{UbBfi<Z@}JN#*osW#LG0Tr0n2GlFLOw zypWDiC{%W}v0)m|cDl1;C#%*2Y@^L46v57dYYO@(1Dl0}jiA3Oq^FR*033nN0lS^3 z=G_3CVUvaeg<Bfxo~f$VgF8%uyhU3TQ-QO6MLz~jF-7_|0c>h^b|&vya}y1Ckp-k+ zA>8X)=z5w`I9H7+6}?L#*32~n+J!(K1S~+a4+EeVz9IO!DGqv+Qn(G$glgT<Tvz** zWk8L7R4kTeer1T2xP8~2<ttND^Dz`MVdvpPylnWN1G{m?&dT~y(Y2Twi|In3JPVk> zf~vZ{QyEBf%&7)2Z87P!u$RN^>v;o6l$plP#B7gvCz2jGdN-U-^bAdb#XFJogCZJS kS?qr#?w|Ry{at_o01Mn{FWyeTY5)KL07*qoM6N<$f(jC|w*UYD literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e97dd429dbf85ee77126c83c1f3e657afc00b550 GIT binary patch literal 653 zcmV;80&@L{P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!FG)l}RCwCNR!wLVK^T4eGqWkOr4gkS zQ5V}NND2nMNh#vhgDIXoNbRvg(o+P%t6n@rgd8f^TfrjaBpzBVo=jA#5`?CpU7|LH z+N5gKR-@z0V1np2R=2Z0_*lNT%)I^HH~TSKCX+b`Aj`vn0%)2B!!Tf)CMcmUp(Jh! z!8VIJ-Q7J1hr`fy9jZ{NEIV&1)vOSeg3;by*zN7|i&UXruVZR*(vf9}3RMdVi2Vs6 zkfOp;)q)LB%Yu%8L3nW+5*<m7w9L;Bp2F25F<jc)BV%mf7~K1JyiYTC)pw=K#iwu{ z%%H7Y!**ygzCJ18-f#jRmtK3W@h$p%^fEd>eZ!t;CkAfc#PJ)W=<1;8Sos2X;+pq? zT5q#+sT9`MKL63;qlF^2F<>@i<5p%&a>FP3v2=Y3*6IiE&G|%P&k6<B3duxr-Mo}I z$!2#)*kU@><jNP%{>C+s%jKAFd&B$rBx4Mr(tMK(&A*V`7NV=^Dc!LPCbw6w)ta24 zL2}}qlAb!N6`bbJ#m6sk;Rm;~%J6EdhQUx5Vk&w%^Y9U~ZCgeI`yvsUPmGUy_SE<6 ziyc0Mp)*5BUr9H);@n)zHGz*oIhBOH{e68n5RJC%>EGbzs((d3pZDKWue=pD<v;xw z{1?#;*}IvL_8La;#p<SE1~1C*p}~t<)N-pURe3#Bjq<{Rc(^8^H2(J8`|R}eU560L nZ{Y-nX^6R@Lf7RH|1Q7)A!IJN8%6_M00000NkvXXu0mjfmY6pz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fa681e049dfaf63115f213c24828868e0b52ce30 GIT binary patch literal 904 zcmV;319$w1P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#DoI2^RCwCFmQhGkQ5b;#yWP#%u<mko zt(H#GvX`(P(w?N33@UvvQ$eRiBv~K?=7Z3-NAt;tj37`SB3Dqdhft_gBG;Qj5>bMi z7<$X(irZYddrs%vH8<zIn?7tm9Jv2~?!Djt|K~sdIn4CbR2~4-lfgmyMI>X0r9htN zIWy0vowzIroFEAGEW(I0<>msG!fA(6&f?M{$6^d1NfO`;<Q9=Zc?609Aqjxdq#ueA zP#%jB0wfW<1}VPS^RUO>algZ<sz2zmgWwfIp^#jNoN2$eqPk}E)cK1`+n@*o<*|rD zNu&s=dl(Lf74;uqrn#)2K41WG27^8_x$d`qc=@c^a{OfPUj9gEI~ak6ut6dZYJ-ZJ z{`t+T3Qn(u114^%`p&)H3yn9PaC@>tIjqJoGxEAr>3@{ROiq4u6crT{Z)4Ke_qdZ3 zmm$>}9bKJ=?2UC;G3LT@wpq578~=V<@y(SVEIu)Dy7BIK{QI<6XJ^0D^pZ$Px)>&g zZEd~P9p`|(t?jl`NuPPzT^IZvE-ADgnmAT-QwDr{{-`pXr30g0C+~_aP`PO3LW-o9 zNfJ@w$5EX#n+5HfSfcT-@==>MxR#frXCX_=Y=_c8p$JQIMJ3y(D51)tx-Ms*s3G3s z@P;93qs~(sokI%MNqNlp*qEchS}4C5mHz&LPVzy@H8tTsaAre*Aj5)3cUZQCykEU* ztD?2yQq|zO%FEuZKmeb24p<4%NF|6*gw58pxuydHgZ7q|W~Y+w@p!oFU9NWe{Pz7j z*;aL>sy!G80AKnG&L*TcQu)&VLZWNRB=00jdRcjy*l4qXt+~k=wGF$6d)2<9Eih6@ zn06%E2AL#<gm$H?3ZYUSGdeQj$j>j>GUsx+cC@BS($LW0Og2A1?~Ap;c&V+u+8uM? zg}tusnscXm;=h!30zFSj?s1CK0q;srb<#u%NlNaWT60j-lSfK=uiNc*SS%LElq@VP zM9x!;amJvL^HgraC}U8xd2`4hRE`v9I8ynJL@&U8zn?Q2jcEr4NKd9bx~4;-DAtpa eu^awB0R{jK4i)x⁡&r0000<MNUMnLSTXsQm_U9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-grid_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ca97f4e92cc19dc9f8aabc3c8b641fdb8a5805af GIT binary patch literal 683 zcmV;c0#yBpP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!O-V#SRCwBA{Qv(y1HAzg!vKI$OKA#N zunDRtD8kvXNh$vq&zr-Diysmd^Z&*3XAgi0QUUA>CZ+?7OiYD`_UyiZZtk52_et_8 zNF6hhdNAK7Fj(o!=g$n>JUj|Kyu1t`%*D;^z{$zEKOi(*1ef{5S_-m|9ZJLbKv2)c z&BegQ#dV33gVTtIo5u;r{>aVEEx^LcngUeAhRqJF!3zw+cR)%IiT?{t9)<s(;lJMR z-+#{i|NoDHi=BHW!~g&3j7*HeKq*eJGy}sgD2>}vV4MlTf)Hc@kk12TvjXvNApZ-D zpPrky2uS-fGBWnem^5)Fko_|wFTV;@-~h#7=7aEWf|f!7Ba%ETQqaQr92}h2K$Zfr z9xNEy**SE;z6W_5Vi6P6ECymM1!?#LW&g%T1ATKJ$_M!zWXW%!7|cfxary>Z+1uFE ziV~+d^BXG*13$k2!{5Ju8D717$pDN;hDQ${9t2u!56n8ghxhG02~_q2h`#~xUs#r* zC{6`MM5O-y`EwPd=hKG|DnRNxpMbz|NPcBzgof5X<oG0%Ww6C5D>DE6n>Ww=f`UO7 zO2M)bkZlCS0w3PJJ&qJj3{;C#a2!I?$WN$4enHuPk<9svZZSo1ij+?oVCF2HKaUw| z2{Sab{z8KoUJPQj6k7HYj8klUXz2VywhX=Or6`}`;4@+K9aZBL3;hRMiX|AQ$W<E@ zutN)PSVAGh#|Tn#Y8mu6BE%=X62}7mq6HZtK2<FJM<O3DU>t4t(h>j!7yw?P4^NGo RT!jDt002ovPDHLkV1gLkDZ>B& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_d.png new file mode 100644 index 0000000000000000000000000000000000000000..934fd501379bbf3d5b04c8a37843fa1a1b99dc56 GIT binary patch literal 930 zcmV;T16}-yP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#L`g(JRCwCFmS0FyaTLeD_uk#RiOxBL zEFEh&36#zLBB4D572!h<2KC@Wlsy(vJxN3az4R3HA}NKTU=e``(L+W;U^SE;Ov$aX z>qBbl{<zHYZo9YN?|0jDtKBAR9XRaXd+zS%ch33!&N*C1N5@V8{X|_TfN&fKK@eav znIMUhEJ|XFi2{qvlFRL`f!%Hg&+{mhghD~rVkq2Cp$zEpdSNXsWh*L_#N%;18ys{o zx2T7O22!El7ex^aN<&&`U{WNr89e^8h{U-Kx%cKl7ku6K5Dd(xo^9H`83!(0L~Zl& z+;%gKYOcV?=UVac$q2i)l4Fn*<^2Mlp1q9ab{V_RTw=%Luer?y=znw<iv(yhcxU29 z7i<nET!)U4{UI10y_|A7-O~-5#xb>*Nocrr4b88HvFGMhcAvCO_uhe`tdgUVF#KLl zQ2y;atTe1d>#koxdBb67<hr7%8MW=_Q#S7PG*%S_N=!i_k;o5Ps;$$#2lZVjnowl1 zz;X0snmrzivm%g4sQE#y+M31!qby&fj>1x8ffg7^v#XlMyi`>+zl~(Akf6;e`YOLi z36vCZvRMQI7gnMu(SHArrfK<kYTM;RG5H8p)5K`nM@?h_Nn5c2Xv0v>TC%QHQJ@aZ z^hQ16{&!G;bfFY<d|U^rnx4PFSk^w8S5Ad3j?9D+pvu4lQ$Y-u>wv8h4W|f<=XgVC zPM!|@u*0?Vg=zJ}muaDWF}IBFPjgJCYz}bA9OPRD_cb<Rhu4z|E5)&2a$oPQC$=$m zUsYL|%6tCdB}KgLbQ?~zwxX}67r})ER900X8VMs5SwdgmeN4Wage=Rfi?HzWd4|_N z>4(i$mde$KfFHcT<J-y#wmF^HLKaY6<HqON8P+d(-Z;EmXXkBge`6EsJRZXrX@yag z7%6eFbrB#Fo+PpYA4@*}m1{{dGh3$kd_LNRHa4bE5cQVs-elg`n91wyjOIC^8h06! z_=nYG9W}(Y1=2Ih6Bq^3{wWB0=qXgt^b(mBYL?7$I1+Zz&R{g0Wj1A_`5>EZc4mhD z$i!2rWhwf#e`x5rOC+5`A|oO#<^WfSxlAV7@>75T03M_}5Wl|ZMF0Q*07*qoM6N<$ Ef;#N8OaK4? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_h.png new file mode 100644 index 0000000000000000000000000000000000000000..07f0aea50c4c03f25ebe04a4eff92973e14ba5ae GIT binary patch literal 1140 zcmV-)1dIELP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$7D+@wRCwCFR$FfyMHHUPdUs>jsZ9cQ zD<@nk(F&C+JOm}>5>=6kM5QKgM1qP}egV%2)c?Q(uZ4&Q<N^{ZR00WwCKPBQKuA?o zoWz^jZQ5?*b-m8+%rIxxYg@_g(ieB6BaipY%(ve;-#N2*^~Q}NLTEu+4h??T(iSxU zEEEd4(L!P1h&%Oqu3oP%Xk74Ieq;m*<_{bcaBkOb=QJS@VvHg21Y*mj0iFaEh;xPz zCW8kR908stI7ggGc)}AmT)p(!iM6GlXPS;v44-8V9jZ;8ef!F5^Y2t13zQ24cp3*# z$&@ExUi$dMxx1D%0rPZ9>pmB4w|MQFuioFiTl@OVg%4IWjf(#VmY@<_GG(!ge!Kj| ziRPW!L^?%d*ZlkY%K6;Wg~rL*bGLLo#WBLMYXMI&Wzyo7ZT_(`6SBjUx-YyjTON67 z(yITtGQRoCkLLwr2KY9vFTMHv)R_&=O=Rdgj|S{8g*Y?cM;J%LlD&2@1TDQjTOL0C ziUsL7a%yTL#l7#ge_fha&Q^1*;W$#5ih!W>V5kGpDj|tPrfIzw*6fQD76`#G4K`#B zvBNXx{<c?_=R?2ix>Tc->!}p(5IevW@MLpfN>OnNNNYXN_SVg3%<<!OLkCdPYp$;q z`u(2gVYlrP>64kDF=fL7rat3{#86Eex+^#{Rj9o-{0f;0jtLgL<(NZ#0)u=7kz;Cp z5HMg$V1YbTb)_gv4`*uzB?t`fSf!x6zy~@ⅅ7ejws{KQaI8AAtmG#Ybm!sE3nz~ zeC$i>&k4?Cx3~z3AI^+oDmo2<fZCu=S@@RwjAjqlySpCVFEO8C2`r5x_l{NEU-&A- zBKj=A0iVh#praTm(R9}C<s^uT?@Ol4$5;Q^G&k3`gO->bMsH0A0)@d@bc+kr+i8Pw zqr864{(SML@bB2C({sR7F}i+h8?CP0RF0FUUfwzj;q8W)#kWD|{J<b?OrJho`{Da5 z6}bdzo&-!i72dJ&@tS2-D{w{S^H1%nJYpatV$b9%ydpa3;V=x{eD}Qz4<}!p{C4@; z@|@FX49hlmzp=YJboA(vnNKcW{L-pg$86hvR+iFV1Y<=v?o7ZVJVD}M^1$bGL$||* za&Kqn7?CylsP)LqX0w%iK5vhXj#gT&)|hMvG7v%}l?~wG%jNP%rBW&JI)X^G_u5nn zsC&~>szJMxdSQ5P&!fGffan-3rD>5=tJNyxR3zp!42cR0p&S~5d|Mn`Qo%k#2&a@l zNL4wB#GFd?BEb`L+G9p}-@N-i5>RNGRFeFaIaNIu=CNmr&eirfpiza~w=V<{`<_RB zvcN@YJC2jfWHJN)Fl#oO>Vq4a!=nA*!Gg3W_J#kS00RKD9}7fpREgmL0000<MNUMn GLSTYm@EXJb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-objects_n.png new file mode 100644 index 0000000000000000000000000000000000000000..22e45dc64d0a53ffc6cca2996a5e7b4b15f9ed16 GIT binary patch literal 666 zcmV;L0%iS)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!JV``BRCwBA{Qv(y1HAzg!vKI$OKA#N z2q{oePz2L9j!pr<L{|^Q1~55BM#c+FOiYD`_w7A^P4Art_sRFEvxnz=Ha52P?Ck6Y z92^`BFbv|eva;@X_3}w3CDa%RWtqH!qJaPZ{;wxw5YTduO>0;01v38v@qdbZ%Ff1K zkL0_LoLroN+c#~H1kxcu{wJ6m3oA=4NI4KQQ=E|4IoJ(=7(_F0aB?K9Te18kD1?CY zVNOoYT$mg%_^pBB96-!Q$XtS!a<Fs2@$!WWPC$i!0r9tG3+JywmIsA06jNp?D;pae zC#GlULgVZw5Pwh0$<suZhnmMi$XtS12AD!W04V_wo0*xJ4@mt3;y)l77^M8T5-3GJ zWnp2t0K^~~<NzyVLt)}DOwAJM0}}f`)IvrqzBx)wOD`goK$cL;n4q))4n6-Vic=tX zfF-`bmYzO!QUHr(|0uR}-mDqOmRcbP?d1#S8IVHZIMlNL6c_kFZ~=%9f_w)wm>Dg2 zk>r>jBl(sxp9Y6V2mp)c2McD+$V|$}ocHC+7nxN{7T<|UN)`l`x4>G5A%u~U!3F3b zhj}xmuOKFPiAhKfKtn-6{0~?;{Nv{4b_Ep>EG(>`JbD1=m<NCU{5|~d-+xfh|Atyh zSuF#tPZ^PlI2iZ?wS*Z-9F~nhCDtEYwG8n&8e!3YXbAmBio?H9A2JeC%dn7WA#%3) zk7gmP;P^`@h>5k7P>TFdOrbz@oDQ4TAV7ct026DhL12{|?f?J)07*qoM6N<$f^=#Z AK>z>% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_d.png new file mode 100644 index 0000000000000000000000000000000000000000..13dfc2e1e0d705ec4bd26503eb6b4b3d009bc971 GIT binary patch literal 963 zcmV;!13dhRP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Wl2OqRCwCFmR(4bQ5eVn+xszBD9zHj zHg)34@-u}f)<s!2B}MJR2n=t`x(qM62n@RLqKKj^FO-Vx3nDUutT}T+SZU1*L8pms zip>^nOU?E!hwGfPO%ThbHXnF6?>UFPzxV&_dCqfW_4W0;05lWVBMv5$$-r?Oc%FwS zh)zKiOgsfD6{0>n+W?hH1-V>~c$wGh(SP&$nkke4^9u@)n3Tl6C|(u}2GMS@=$S04 zFy26F^!<V$fI(?U;|+|Os8B$TbvA*BTQT%jV<W~Kj)nQVhHJRFuNZY%1~!^6pI<(k zNmN$^wGRxy<#yvmYb%@g-M)^#D-BRi{y+*T4LUlTTG3Tkv+R3T1by4ph0P@CJU$;@ z5wPptL!^deC^)eP<u9MGk<NWj+x`zUs<O1S6uV1G@S(RCiH;cp0g5kPMRHCNj5qGH zxyn1Yd=|$FwOXyvsMXk+n~M!fCG0~(*b<6tPL2X8TK2fGwpOL@-*&N(>a<$+-1Oi9 ze$38d-jD=UC<NX=&DL4XlR5P@D}C49L(feXQffH{15(wg;Cs#BLsPIldIZas9oXa< zW8wYhArXs8s_NXCDvXYf!tMTw<0cc^v)RnBIa)CD{U=0?h{u*U@FdHi%hck+TEa_J zGG%ER>^1IN08+a%GKI4vF5De<<J8G=xPxTP@deL4fz>OGsx%snn4h0t(t)a~tD)BF zaJ1|QGPAO9?C3G1ZPlW(qGI)lL{(<%_1Ku0gh1feqP>~vDYRK$BU6`-{K6u(cDSsJ z)p2`!`|6-kl{@qD;Flyw#QlD0VZY;b2SOxE_m>`kMAqj>+oDG6nAUTrwPWQ_s#2%X zKul1Eff2AI1z_**M<4OxqC#-I04WeaKnjCt9T$=h?e6)=q7n0d8daH*o{ov}aZ)Y< z$TcH867THnMDivT$<i$L5#eh|<i`mKIC$tF20sm><yi|(S60Rbr7Ecn3=aM^M0pvR z_&hOAT9puK&fx~lo{8_+zKunL9-mbGKcO`>f9?{rrP0%fTK}LursMOK*}N3Waj`cV zS{9dTYuDV!<vh1O*OWz2njq^ARfr0w@4Js(594i<E*Bk~yda9Esp*+!tF=QfkWNC& lg5x<_1o7ZHiJN{4FaVi8Q$l47DhB`n002ovPDHLkV1o5!$NK;P literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_h.png new file mode 100644 index 0000000000000000000000000000000000000000..47cc46b216014c3bd4b3133d39d1cd85893f32c0 GIT binary patch literal 1178 zcmV;L1ZDe)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$JV``BRCwCFmd|e+MHI)M&91$TNwQH~ z+jY{&Nu0z%P@G7}u@q?&B$|keB`ysIlv{xt+&Ccte*pB*3Xa^`9x7D=q(}_}(WoV< zsn@}=Yhq#tC*D;W)gpGii@iI{%r?I)uOlR!k)GBw-kr~U-@Nx`EV0LrJpeF=jfd8^ zBDR|h0lM98-syI?ocL6hd0Cd{7#-<+S9doct=4iV;S{6-&maV#X&Rt2@U4!G@GumC zu4@1mYwMv%2f|~Ju7j>&yj4H-quaM`&ySCfFZhT266t^U%~!$1qZL6_Ro-s57yAbN z;=7-GzGMp35fGkLCs3@kR;x9&xG4Hc<&tl8bu~izKYe>6xbf&=#11;&VP}Cn<xE<* z`{fr?W^)h@;W3yEntr;<R4OHO@jN^$7Ck>ME>1oDJ@z`!*`UkY6AQlm&d;$Q?g-Cz zwu66fZHDlCrfLb#P)Y)+iLECRSY_E44u|2=YnLFuy)Cq1Wdu6;-qi*7@T<j{k3PLi z`t6LBGn<ESs8Zr*4NJFJET$@x>&Y3{c^CMHh5+v!WHK4(<XHNjvXkXG{yg2|gE5i~ z4OP;hrGaF)*9;kp#e#~ec!oX0@LMzr_m`LHIOAxCbJYqU<vACPMuWJ;+HFj8C@})n zkwzoREKhS=TLR14Aifr-{ek{|=yp1xd~Xx1wF*30TZ`n*xp;1Wmt!mkQK~md8_iH! z({LC!kSfO4g4Jr3pSyAe^7%aM?d`!^moL-*mNrC)E#1TEUFO*i8>}WXGr8mrbau2$ zADTch%22B5M5zXi9FoufB@ksUT$f#SwfTE-1MVzFA@Is``1*s7;rjI(upp+%d&*T4 z_fpqmmJvu~PwSK>66;ju*w`3#m(_Z*V$ILb!^OTncyoH1K4)iV!Q0~n{Qd%|JdFd% zC=gbJjFi!^i;r7Azb#zs?S(eS%b=?NocMTue;<CizYINIFN}<i(y^)ODIj@_^LeIz zt^=c~iFsDyV?x*IpkawbB3P?c`GLVfs3;03*eVs}5c=@J0~(8OghHU;@oLRDp-3KA zDivQ`j87b&@6c!&L`P-txD^VLK=P8Y*$v<eu%f8&M>-A3R0>8$0@&p*fugFQDh8P3 z@n|3bvMdR>WODWM9+pAke;pkqO!`Ths%&#OKq?f##<F0uvB0v1t^B%zBeWCTo(psl zq#ecs$H4?vyOH^m6Gbr+3WaVpI%$kRIu>n`EDPkB&2E}TpwHJ2l4LB1VHms^hAxQy zW?1kK4iIkya=9&EZ*Nbw{-k+vo*qS8I6QH!5omXJ*Mo!P{nHk7qOfGM5wG{cwfa&> z&r^g>zc)CYPHA#-^0WVXaM#a`j6Fq7%hvyYdpNi`oFSAdMQ5y(?kTwdmr5nxfm5W_ stz0hC3pd%*o5f;r4%?aC@c$KH0I-t!5cCM7MF0Q*07*qoM6N<$f^S7Iq5uE@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/snap-terrain_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4d8534c5ea2a76da3c4e0b28f8b4901db7c85492 GIT binary patch literal 967 zcmV;&133JNP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#X-PyuRCwC#R!vM3Q4pSOch~K1t1VPZ z2}n!f0K^C=DiY!kQNuwr#=wOjCVJ5XB>^>D*5E}k5segMJP<t?G#W0(1BqfZ;m-&} zgakC9_|wE#PKI{dLhHO`*YMZU5`PXl$z=NGy}s{#^JeBP6%L2tPm2crg8T>ltwJw$ zWZ3Kx7oR<Mp$kpz^`^^1WL&(rq&gG|iI|KwilST^jppvF=g%%i1N`8ch@B|{?nXnj z4OLrOcKA5Q>DqW+unB@5h{dsZ;9yxJnMb95MR-M%$wX}KnfmjMoQ@OqdOe`t(;M_T zXz!CT&+|37MyEuPS~aN3vS{ZH$ji$G3}u5L2x)>|0IgQr(D9)CYsbU(S~5oeI$X=4 zu8Fii9V5$Ti^T%Aw2i=V9E^{TgU=_(6FQwP6U``U00HNdVTccPHg2ZKvYLc6IoS%s z??&O#;|`df_9D1WR)%2{YwGGN2!QHC2}RpG7N#P@FfcecG?(2O4ltX|FfcF(fj|IQ zmTg9ND&8}PVORg7_EsuxP=%$f{OA$*{KXBPsVUgAt5}|MyWP;;-2(=LKGkeq4<66A zrtz^cplRl<0v28^Q;{$;nT)c|m%hG!=zRJF?2b%uwzfcvvlWJi21$~AS3)}*37B4q zWq$5vzy<(V_zUKbRaJu3YK8s#N+8Ly0ZL0tAt}j<_dPA%ao!}0gR7}iF%7eo+^63k zSTHf|^+MN+mynW@0(QGi_7xRw2U5par<asvAw^>YRavIn?ckFnkWhU->F0d^>o*v; zFcfUf2MPVbnVC6YtYZvsA!hSQ1uz^BQ<2mqNe3BF0hS~`xF$cs$jB&UXJrBT0FeBC z@Jn;Rq>giPvhgakbrqFW36a_%9f_qN@G#|>0)}NtSpbV+CZX=>?FFOJ2#E;^@*(89 zP`Aa#v8`YWHjnpfldRTT=qXncYC(gb7Jsg^wBDW|pwUv=;YbI8=j8)2G%X*7X67tW zmupgvMlcvS<ZQmyiGT5{p(d})5>Vx7(<)7ySEF&_)M;$$hR%52P2Bs_jjLBS;ZLuc z&9Q*8i|&L{@*wKdH6IssMgjGa<RYMv>Xc-jq%>@5nLJ?QcovtmM0$l2E+;|dF!6vc ps974~mxx6lt5E;U$Mz2a1^_~YAtWCNzghqQ002ovPDHLkV1g8|x=a87 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a879682d19220c8533a26bf7c92d97bd7a382509 GIT binary patch literal 945 zcmV;i15W&jP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Q%OWYRCwCFR!L7&Q562(Of_II0>vp+ zSlS3gV?d4W)dgz&0S45BI7H(@Lrh$2R6v?IEKJ1UhOlv`5r}nb6qh90$RHqt6zPM$ z4xV#pp@>Kyv7F}RzI$Hp{qC8*bCp0KPywKm9v^ZbilRW%H0Zhx(=bDZY1HZr=&?*+ zSy?#>3k#vDDsm+g6XU+IiOEg|6~H}yKk^F-<V3lWSS*G&uV4GbEVeM$fGoVfVHgll zc4@8w(_&f{RIIZYWZb%gD(uy4|NQED9X@w-FU<X?j^g;8r)vVv%F>>j=P=ySgCZyk z56`=9)We~SOe7NjA9Sj{8^uWlRrM!u=HmzW&mNQc^vfRD5gU%<4tP~id!P?nl0fN> zjVN!pjk#zPyUsVFWcvouJ0l^?&CMb10(8}wCdUS4bHkfYu5^%0CSh47ic1UNe2c&{ zGY&hp4TfVtqa<m2_8c>j8Tg584jnnXHc*duCpHrh#=?ll6U3t`b0`YNrsV*MO+})2 z90!4xRs`P#;q{i_<mpoqZ#BieuH375cS9viwQq*ZPErafLgGr_FVROgZ52Jccc8yt z*3_M=mqT2<&_L7|Ulwn7G(=!4>Djfjv$HaN^60U6VRZC|0H))eZYEiQ`ld!?{GuvI z(l1l@mF6nxAKbl%;o*7R$jGRS6%~8Q^)UjCOGr#pb#JMXvXrgJl$QI==>60y@0FDm zs6JRjU~%j_uwSxW`l+V7b8k2thHcLxVn>$gn(j9(0#;Sx^0g~eLQ#n`pN-F{soIi0 zowcRBD4m6Y=7$&<93*0hEV_~`WWZ?D5s&E9!VI0jQda7^<{p$6dGb7HZ+n4Y@coi+ z40!44JfO5M(|~1K)H`)Kz@N8~th)?b$)sT0w$x$cEn=mTV)<5GQ!OQu3CJU_`P=o! zb?0WLk-~SJg@lqiq<3~{8t`9tEu9(0jWD$_({SN)9dcQ4mNGuakX^dk<xbB`<*bv6 z;?&gIGGF*qhg=q%rJOX%N(7P#O7~FLpjvA4xUfxJ=3EwRKsHxcm0}Q@{5dJtLs!;v z390kspJ(M!l37d2;&1YOXo!apS!&i!g{M2ay59N>rYcGXO%^N2frRL({Vl)%Dp5;R Tt|9Z~00000NkvXXu0mjfe?GgR literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d11d45932130c90a4e78678996fabe7809de6bca GIT binary patch literal 1203 zcmV;k1WfyhP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$RY^oaRCwCFR^Mw}MHHTyJNL)#ChgWF zyLqvKn;^C+sGu7Vw5ik(r9~QS*ZSg14frqgp$Y~;!AF(ahyE~_7DbC-(zhxs+NWv? zeNu?qXu32s+wAV$yZ7Fi@toPcny#DOM%WG<F8AC!^UXQuJKqfb<@)tuLP!oQNqRp@ zVDlUS)9G|_FrDr>;##qoEEbD7E|pB~8W<o-ggpmEoYmW_Nv;$jLI|QBLT)7(;sK~4 zQVK#S>piHF1o60%l1KsYO!gjBDuQ@iA}B;EJ7CVuo}B#S_us~&_s23LKfif$_R9YQ zmV^N|L@KtlZ$3ITx&CKie1~I^$ngE^4^K_i>vbxCfSu;I8safT7CPGYR{87J_)gm- zPmGQIc=X+mKgc|}Cy(#$>aFMQ-P<rdug;K&vcnpYMK}!eEW%JTxYgXX)faa{WRLVG z%KN6@%2z5CK00;mr(OMtD*9!$^g<bYzVFj^^Z#RjgLptBrFTHv6~M3U@3%NH6Y;9+ zx<)7jjmKhPvM*i<s?|iC$>-NxH*az-97@Ica~H-v&vQmbN0whYFtOaa9JVFmfj9`w zsCIp7*zv1zlkaCl_W6Fm41-DAwnB%<xM_0Y6C;oQn&%m3PM<kgC={~sc>MOE!-uYc zpAt$5H<-G=Z3GYrkVCOW4PrZf&_DV?pC*B7&|dgKKl<<bB#-_&)U{M94deHhCnvwF zxivof{_IQ;K-xF0hd4S?Au&?O_ZAe7wi$^Cn)-+SLi-KIWDtfn`mQW49)<^X-C8OL z0t&%W#+ahb0wNxlooWi>ov*Iy=lR!8z}^}=j(lGnnL$u>_UyS=-D(w2D+0CGCr2uk zx>2wD3@zr^!Zb~(ngRhbD0Tl}{<{pNQ!d1PjehCkrKz<$cgC7POPSHpoA4uj`kAK+ z)oPU^80UrrLTKSdJk4II_t0MQzh78<&#QaJ#zxtSfFj@gmlux!;2!i3rBc7<j=!<s z)@oYM1!RSRoHIrnq!qHcbM>zbb{dcaWf+E@HFsg|;6}NehI%<Q*Bx#GE*;AqUBI@& zbuEYvOh9zXvP=mzl@W&2+KcH`T@E%%RV)?@E`NJDyRx#9-TY~2XlQBr^=XLELqOu# zws0ILgqD(;Ku|V}Tx~jOSc8?F<qIY%tV`hgb`1J(>XoU_omi}hi#(pm+=P}fu-9$N z6qaR$mdORqX}7jM+FHrcX*{}n_wvMniEDM==P+9r3+8GGv~61=AZ$*E4o8+wuWFug z4>GdZXO~e5KmFvhnOe<D4Gs?6*t>6U0a=M)j566^sOr!HS_W13nzoF$XHJ}kaA+8Y z478Rb)i9m#P~FzT?~tZOuD3Ce$w&|ygs8`c#COBM{dbPC8EQk3wh0=^qcXWE3}!S~ z%BXyhb+jVk1sKlBWFnE+HZYz#V39a?y-|Wf=?gcqcfPc~o&$SmH~hZ^7yzF|fk0<W RI&=U4002ovPDHLkV1g6zLlOW0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/snapping-settings_n.png new file mode 100644 index 0000000000000000000000000000000000000000..caac7467dabc9770fa3bff3ec93a5c875d81a0f2 GIT binary patch literal 714 zcmV;*0yX`KP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!Y)M2xRCwC#md{HRVHn5XcV}>R=bcqM zgw)BvYt|+3QU_hgf>IEP{(z3!cJLS#o!p3xAP~V0m5UW27F2>O%X*LK9N49(PP(A8 zwlnYB^T_kU>tMUit|mk;JUlz!VfOPrzup<HlwyB-CD<XX|ImLAYS+8j(|uywpBu>J z!!XQh-Lk9)<>JzfxUkP(R`(X^;H9B_%5}3|I?c4<^@YpB50MYX_dUI?aI-=P=DP0P z#W%0pJ>li>I_0Lu@HS<O?;EsOF5eQuV_@IrpU>R`Dfw!ya38LJpuX_--A&Zu_D0>B zLhWt~fx)n!U~Lpp6}0c*yE;1BC%{jF{Q{Hy=LfHY?*uC?FTO7|6)8fK$rN*(WEa&h z1lt1bDC(QOpCi2ufO+s}LsF_wq$grS&-V4*LC-lqMo}uV<EUTEWF|>Rz-iRrEWcK* z`VMU3!=D<>j!_C#M-M5jxWO4{Is~1hpwOiGd_(Bt=_&nF>RlTer&>M&e~PqT@&)>T zi)tRU66ua~0TAc)DqTJe!tkY%(q36Eu25$OCU8&C%Gw%29564c4Ub-#tyI=WNPYqx zJ4PUG^gu{iid|EwcMFl_D1}D=s8jlkfDVB$oizdm<YPnep-ljegCCFFn>B#Aws4jS zrgm$<M2tvr=V`EyFcxRP<18|$;R`xAOkgeAv8}w?N-+2XzVCn44Fyf3XR&4gRnx~{ zbkWgf(ct487N|=#G-w3eZvT2;gZh2COLp`TXH&Q0+Mx8RM(<Ob#b3Hs8Xg#3s#<=_ wG&m@vani`>SR4dYgM(%s<cINv|670o0Hu}Kf=FSmumAu607*qoM6N<$f}>_sJpcdz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/translate_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/translate_d.png new file mode 100644 index 0000000000000000000000000000000000000000..39a3487bd29638b64a497f19c5476e7e2767a051 GIT binary patch literal 991 zcmV<510ei~P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#fk{L`RCwC7mdkGwR}{uS+v6GA*fDmT z*alJ!q)8kgkn$>_i!MQ3R0<n}5VT!n!3y{jSW+P*5Q^9$#0C}?U9=J`00l%E@_^U| zTBoszJ&6-LgB>TH89&B7Gdv`Oi4*Ex>C?RL?|%25bI%wC2M2or1W7y^)C>j#%w{u* z0mpK2mgD*v4Pxb3zP<fxxLht6jYc%GWwTj+DZd=lKssb+PY)bTO<)+Np`dcPjG>_+ zULU^Z<o|$ZP3x26IOrg4y6QnfsS#ww#ShoAEFm;G`QiFbFo^t8uI9HUlL>2Dkf}@r zw<FiFQjnmk75(1)Tna)msRI{MbC`@o5DAAr+sB$f52JrzQjB6s3}IzCuiuNsV_;~c ziPtk2AD@7I!v>5`Jo|5;;p8w%vWh3dBLWp5BxX>psQ8NKA<PPR9tz{g;ls!;ui$ko z_GO@#i3ucfA}WfC2f`Dia!JHvakxDmw6uD$Fuy=<Z0HIE5a{f{pCcn*26`NQfU;6W znH=$~7t`s#k<I1cb~o$K)0qr*Z10AwDCp}uh`*xInn6()badBA+&Xp}MM=fa`v$Pp z-VLkO3L{17^gmM+3^KNLbU~6NI9<)~d%YMN8(SYpi!g;o>To(ylC<*@7SkEDP-ms0 zf^aB=Et|IxMAlUx*f+hsn0WSleV}}92`tO%`<6C!$%Fuo+}Lb3yb(pr2m)IDe%*~k z&NO!&T>+q4#mg5jK2F?vQ&%h&$wm6DBU!-`txu6$s;UZx5<N^w>+v+}Nol6()Yfdd zQ22C*SPkN&t;uS$60C$WfqWEYyV`f_AVn#{)8e6UU%~XvYj7O=9*nmQ`}gm|=1rT> z>hrH1G?#jtoPM7JN{+?+t1!~}5==%a10)B@MH26h!cJ=5<AZx|4>taI99R$N!qtbE zoflE1sgQ>cVCeA;gp3u~sorREfR3c5cke4>Dda~}u*e0toNgG~w$}&!et#O5N8_+a z1q3()u3Y*B{r#6~zWdwOPMF**JSTpD@90mk@H;+TBSVc37cvE$z3~)a&5$cB>`n_o zdOz#D`}}EioI1C9xiHlz55L|Fq22AkK;JeT+U>)42ijKcq#i^EQJ%yX7c;z83E$VG zi)X%VSmB$Soz)I;#zH?18Szc<-rajV$C<%$79FVlU^J-3N&Wu{FaRV4bJ+GiPl*5k N002ovPDHLkV1hLh!_oi% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/translate_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/translate_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e08379f4bddffd2224afdf58130eaf44501071 GIT binary patch literal 1175 zcmV;I1Zew-P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$IY~r8RCwC7R$WgUM-;u@9h(K)@dCyy z#t^<#K>^{mq0m-ps<sJHUQksD(MoOA=RWioRDH`+^CQ})RH_uEQY#`AXhlWp6ezKs z4`N7caEQUiUK@LN?fvQ<6M_TVJXB(&BYSpd&Y5%XovXFY%*+S?Fpc2hbcTqyMhwH4 z?(OU4&vx~mib^8B!EdZ@OcO8-J`m^tU8|gm5ar&8#dt>74bW8WrMPkH$DZ|Atm$wr zX0zny>(~1Z=U2bGI+5Jm<o;_v)nH7+0Hs_8RZ}hTdw52eU77zdm&<u1Nn+@;a5&;k zY$XHPT$V&4VLrLFH9j{x*Kf6-a#IbW={gv?im<jmw=zGnlgqY+lgq7*jg5OgUrXWM z{rg>nu&}bUEIf?G`dls-Jb1V^rdG<URvc7=!D0ZFTGXr&fBK|fE30rP9_gd5R{y!< zWo$NXaBv9X@pvGSh>uN8P2pNy`1|&faC|@IrW%+dy*jbN?9xr4xL@K`g$i!<K2ELk zYinygZGJzrcXWU#ie0>i=gy6ugW=&J2!%ppRy<ULp|&*LIzNlyMRbEFLo^UB_c-;a zwOA~+2!a4MJY`9e+DC#xD3(fa@xld2r_;fmogJ>0gK|F$9ST)6OMLR~=g}))UiqOS zt8i)b;_besfr{Jhw%ZtniAJNmrm3!hR|g@VFMtolsndmph2U`>s=;7vp`un!Kq)H> zp5Ezg@dC|}A}MS-z3cJgy{M?FBN~mg<A8jj0Q>vJs@OYkjYA@l7(32IH4YqUwDMkr zDCW*M8=Q=T5Kt8rQcreTaL&41PRM2UT<LVi+bXo9$o_M&k=NgVI!7J9ytLeP$W!G; z4MH!Is;XeKy;zd;xh$6;w!Q%>=17)g*vsyLBulu_G;NGci-cE+x49YYb~`v6cFa)* zFbdTwL)TRv(>&lpA*>24!{ei#s~1_8NO%Yb4IOJ|*SouW_R5OP;w7{X^bg#XW!beI zn*YGv+55oW<jXfVH>a@rQf+O1k#>ZmLu&_)H2tJda$Dr8y~@uQH5r>@<gUw^E))sN z07qEUL0HAfvWjgx!I|k~u)*z0xqU5l4KOBZKHrJxkb`@qo)EhB)8hGJLE<$G70J8; zTgh7!xw?B$$Ffui8te%Cb=paFeMOdq!uGa6j75&P`9GM>e(^{Mb424wQ9FKHc+{23 zl*aY40<39>?_YZJ`^m|#;#TeD*KfZ5mO*EH=h;x;?e|vedxok4wVd?-2}hZn!u=KA zoMbS;)n>>T%H_+!xRq-<{DZ?c20#Add5^PNS9N4!S?D$Dzx<gvUtiCOoq{LS-F_xM z`dT2RA9Gwg`ak+?^=0o5D$m5w(R$a8TKA_DJvVC?)xP_`!+4e>5$^}jdpxJ}!I00> p9Ek+)-nHG`-D$+Bew_R(zyK$5T}i10cAfwL002ovPDHLkV1g~gN9F(k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/translate_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/translate_n.png new file mode 100644 index 0000000000000000000000000000000000000000..17f1972476743a77b72321f6a73a326a0f5ab8fd GIT binary patch literal 1013 zcmV<R0}A|!P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#mq|oHRCwCFRZC10K^XpLU)@S61s|BU zBw&kC3sMxNCO+x|d_)wL#6%B1E_zc>#*4<28V|;+iSa5XMo$Kf5fdSziCz?=_`)X! z71&bR-R^eQf5<Fps#es5NxtmN?#%zq|NW08wr#^y3<0KSOc{}s{~}yj_0y)7?QQGA z)z$CLojLWAtv9u9Q}GeMas65cU!TLrNnDTovq$S&wnVd8>oiU)jvPJaTx=d9#t0FW zI6sVN{V(Y()7)bi#^Qm2&vlqMetbokH?l0V&-IAC9*>934{>|spNLG;Y(>D(_wO4_ zGgGy3OX~u+>h?7@HnPvl7(rXx4p3wzx}jxj0}luJE27wnmC;NlQ;C2?BGEiNG#rv7 zsktgNhlD~Q;&Qp-)61s&>S}8sQWF6|Aj~V}S9XQTc*BSYkhe${l@&P>@cW5PDZ#O{ zrnVN6V`I>?ZY}yjb}d}oFcXi8e?_!%RlI%Q{{3qi8D`zZKroQ@`AUn}t)KQjt3vYf zaA*!_S^`Q-ec<=|ArJ`e=0w7zh<Jw0tgl;0GKK-Vp}SJ4bP$aZ%d$#dzkapU9|(Y! zNWkc5a-1v{i=k6+0ME&jCSvJaQ4y59UN4gn^t3(~vrEFbpyAPSP1EL<m6fsi1lfZ5 z^T1PFT+y^?b38vOq(agS4h}-ct=mYHfpj`ehlhs*!_ZlCqzJvQ?}f_9WLW`8mf+jh zZ;(tT0n0$(woOEq*&_K(HMcXMd>-_s<m#37k27Y?+7}L2ze7UFwyBC5d0C7S3GH}M zk*5Q*@e@kgt7#*>54*eGvx^|c5SfA=H7;M_>+ZTo@X(jpfgrqj*;hXJWnf5>Wm}SD z8w*xb6lGKp#S&fDce-3^FUnf1y4@eRWFiNf+rK3e1n~r=*~)0c5;9&#+Q?}`Hy~?e zfhu%-52iOL!bF%OxQw`6s(1P7HD|jz@4R6I1!J_jd3_vZIBc2}L`muwDP5CFCVNtl zRJrCR-%4ug7cqG>#D!EUb-DA_&3@Lo_#V?ClYY@eeR%6jEOeAo7Fg|3&;2f*vW_H_ z8(>9y@bQD5cHR+4PQSAYdL#<M2@vgmOq}zSiubzjc0R|~)ACATqVk_Sesnp<xZqqM z2k@t?V~12uB62y59N8d`*u@Pyl3844yHM5<E|nvP6PH3nS?AA^BQ5-?U`Y;vmGiWI jv!6Qkl271_9|8;jx1oCs&QuP200000NkvXXu0mjf*?HMi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_d.png new file mode 100644 index 0000000000000000000000000000000000000000..14e658f0cab8c4ad5ad1c643acb35ae14fe9eea1 GIT binary patch literal 1040 zcmV+r1n>KaP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#vPnciRCwCNR$FM3XBd9+=Ra7R#>SXn zEY-iYiB_!5JZ&(~R>WprRGb5$OmfjFtwTkIUMTfKRJ_@SR75;mZ5=G4$S&Mku!0(@ zQ(<~xlxPp4*4AlT+Z=trH?3H0t#zb(;pP9n<p00tJ$=vn8Y(I(+yH!}I+7p;g8@dP z5wa{pRn(xODn+sYOwm+*;=~+SEEY(Tgk(u;>vO%Owaq6$8E|S|9@5NawkTN=iA3<{ z#fv(-B|J<vpb&Asq9|Zc5z=G>_KK=$kTA@mkaNQhYM`hNJ1D2~{{S7ddY&{tf$!9L z+^D~W2lpF3_&?oZhSRBIap6J~cozP5&=Utvqq_1euSbl~7yyW)t1HC6IUF{AwzRZi zclj@<KK?s4Z7W0W)JdNg-ctvwaPr7+NFA9%G$!HI%l01aKsXd;ee|%I_36_VA{Q6B zySuSveL1dO_9GgNeg^0XAMAE3Iyzs&<;q6p_;DcK#58v<_8r~>*Q6}o3ynr&2m~JT zIwwcR-W~f;f8_?k#6Uv=E#2T{z?I*9gS`iKqLbDt9jX$eSgh&HEWGbg@!spsF7|MY z&4Q!*D-jBX`UM^^r;WiznA1#HQ(DaXG*c>K<k5ZdsBFzo33egiUio77G}c|)e@0&Z zWQ?NxhbRT?Sx=`K)YF3Hix7*)P<8kW@0}D*Ra5#^n{71mXXUfbTks`<4;rw_`z>Oz zD58-lhyRnpo8`>H*K=p1uI4K4-24aLvhR_RF_!YC_ED3`gda9~6Rqo6D;gd>hGU!^ z@mK;#k_^N`hCXK-JswZFK4AiEcKZjw)4y`VU+2e_OZ6D9XTY;^88ix0gu-UAj6zm+ zCK*!<*Ayr2-oA@NzaB$-dk0pRtU{{U1exAdRgLm5b2Asx)YSCxdn&rm`mO6wN!RE2 z!D?h?Iyi5IE0!YFG!h-{oe10s;9S)O1nvc4r2_N*SPYxP&UuvRs)<~_KNt+kNLHH_ zYd4qRrvGpFFJH$W=l<&3s;F|uQaG1*=3~a3nQse+#}f*_xZ^{nL1Co`PyQ@7a@|u9 z4u`oo;)0vv@jkPV2%yw64{4eTjX{k=2j#@mhni+!1H@X49pd`L@eU{w;glIvVMF@^ zWYGTHAIM{2y(}B~3dE>7dp+$fl7vwb?>{!+(HTP<Q-t*cdGN{oRPg1nH*rnXf^9F_ zI8Vuz$IZ>+5tNBlivE51%2!)kqbrKad}@@9TpCG`AZgJX0R{k|zabrg%Bi~m0000< KMNUMnLSTZ^gYAO= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_h.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2fb1f02d9ec0ae4933c8a4f18f80d972050f1b GIT binary patch literal 1195 zcmV;c1XTNpP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$O-V#SRCwCFmfuetRTRh1+?k!3We0X( zslWnDyZo>aNl0x?O&ZerGc~3>SR2w}^vQq0m^86TpM3Md2OoT}sWGJ{Bv?gXj434& z{88Gbq=7Dxh16wRb(bIe19s+K&$+ux76Q}<ckkw#*_k_YK6B1J_ncAgOiwogz#z(o z@{c6ynnu8eh6cO7p`q-=zv6K_9*+-dTyVQK5CGviWrq?cwj8rHApl(01yO<6auo3p zDu8nrKw;&F3J%261n0nA#4}!gC>KCHjT00-<s2$9h^HYNJ$0c{gLu+Y7p3fS!*cAg zIXd!f-%@nx*_GtV!JYHEp(pDCbqhz19=Ut^%~uwl4lH;|adsUC6lFX1%P)rerhl7$ zo&Kwunk4}JcOBQHqtEii>6lKXYsbDBdw1gIMD*N+v!l=Tb|?0P%A^&*1VPDL3~^Ck zUAf+O``6n89^I2=Dg!6y*hS%kCTV?by^WO3WE$%a)IY@M%qyQ?eShNSWN>qHQz^B# zCFBr~apB60;w8E}eQ&_BtR&uGcXf4|!C(`3JRa!oISiLS{s_YDhvYd)QJAr|y4I<C zJW3!?w={O+hj*qXe@Adm;XCX~4)Ua@j=O!b|I~?Ls#I^BISm&te+ZdO7SL;y4z_KB z=Ftl0dC`50sn6gNnLc0D>aCw{ok?fX>Sk^`m^j4KTuz>%>!mgF$={9DmIdd|za`Ht zs|xrUhv-5SE`D%HUL!Ay1J_aN(@#EYtv?XBZ<xkvDwUQ=YMO?tGIguq5s&f0nwGxN zvExVY2&^7AM!u6bhC9Mwphvq2Lkc~;JyOSCd;wBxsm}hFPyC!iFL7-d=E0V?c@8C# z;Bue3g^Q%-P*buc)H*da`3uZFcnEK!$Bj*m=$*bzK8(iDd*|hgCH_f3?9Uij)z;&j zlLrYou$L5pRO6AS4qX^JSySVe>0GML!|jpTnYr%!_hzi-mLR<J>M785LvAm>?T6O3 zR{1jFjzbWci@@;JuK^QkqxbpV5!2K+R8<wGX*!ywaoV7Hy8tW6Bq-mHja>?bLc8v( zL}I0Ca(rq4w?<d6xk-kww$=u3l?j=27Uz=?nT^20A5kz2{YiUw$7qe;S|yOz>vb&4 zm-7&`WGpT&0`*f&<J}uujlVj5^0of!k=gn7*rVlR<3Emfl+H88HVm(^)Y8^G9}ER& z=o)6QV5-V7(I_{?PTRyQw6?vgy{a-E?mRe;>m1E)Y^oSuCZ7>vtAsHGQAJZFEbk>A zU3!6?wq<MMg=(~4$;5C;16T;SgHVzvY5NNPD5M0{br09_Ar-sX&3BnXk9*os8RLeo z3-o|WoKlQhv1C)?Fog*WxeUQxHf3?(`B43*wxlpcsGtf2?AyBbGe;~91mAZ}|Nn+s zXl<2*5|6%nlk4m2w$JA)J1~_>$q#PI-fPK3Vh~laFZ_Q67yu6ZzibQqjX(eZ002ov JPDHLkV1he}K$`#n literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/visibility-toggle_n.png new file mode 100644 index 0000000000000000000000000000000000000000..25a2d20d8b1d1f7c29fff6661be681d206e88759 GIT binary patch literal 790 zcmV+x1L^#UP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!xJg7oRCwC#R!wLVK@@(oyR#`|+k~pM z2E;^rNLmpOLPfADmZE7j7Ee-?VjGcq5%DZlJPP8$_ELymBvz&1FP2nVQNbdm2tB3f znhH6ECXJf@B)h)ZPQzL^l2}lbJoqv*Z{ED`y?Jkk5M9^dPm2J5LCU0M(tnp!vQpN$ zJ;vB{u-S=2CnA8@Z(*t;MiIAPjzmU(MKrB`%+HC{<G@Ifkda;Is^p42!m&If@AC(A zy$OHE2oX+?MTS587HROz*-li4cqUGcLXsi`UZMSFoNo~&v3ca#(+Qkwg-9)HDvDBw zQmRUl<o5c!PM6CCI0vuK2fe+Qz_Zn3tT`NL-zv*gK)e=+5IJ-tu#34VsgY!x+)k<F z_P6@s;)U~&$z%W>8mO#Pkcj5j49w1MSGG%(I`Q0$APNJ_O(9~-M#AWK+H5vB+U7Ub zcEt`kEeEQq!sRPhmu)D`+t=d_F!c5J@4-yNny$^D+QzpRR$q02>lJV5nhwK}7slF+ zjU`jr64lk~pl+MTKx5->7O?-}{d?DVmXZ~RCF#)Juu5fmWBlznsGnkRygdLLYij{@ zKR{6w=;%0MoJps@Ks+92`YDrNmf=sGTJV!$dndbslNA-V9qq>g;M!CR?<OW88jTt@ zRaFi+ux~Ha)NFv*%x8G?_z5HuiMjM#x@BlE9OKve6|u;Oi~3HPoazp)$EFDpLfTjd zH#F3P)8PON1m@=#Ael;G5YtAjQKpi!vt19ucUV1hh<Q$$E&6dVJqEP9=UfZcxd+J) zo9*!I9O~X=v)ShZeK!Y9SIV;c#z(DG(v`YX-i<PE4#JV==U^Ew@hlh3U27(lig=$j zlY=Y|K(rvu@%<$}Y9&4R7NT=!IUayuBGpa7X{M}Ut)#HZ@tTB`|APK^q~8S?0DEZg ULx7U@#Q*>R07*qoM6N<$f<<0)0ssI2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_d.png b/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_d.png new file mode 100644 index 0000000000000000000000000000000000000000..bafcde2ba7482468f3140792200c4f94376a4ca0 GIT binary patch literal 1203 zcmV;k1WfyhP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$RY^oaRCwCFR%>jNRTzH0Yj+8Qf@4c( zwUoAVf#DLi8HVfv31mQ&I*G)%_`{jVACtvI68<<bVnBZkLpEd3C{i&qP?~H5WpSI4 zRY(@tC5)j<>NMTP#@5m4diz?>Ii+34y0nXyw>jzeo%5adIp=w=TvJn15dbS0I;222 zj)Ncw5JeHPBs(NoGK&;2T_x)_ZZtrxR)gnxq)Miz@9U<fXRH*I0d6iV#Hy?;cA`{C zAP_*u)vG#oEqcRL15%;-B}oDUrIe-`Fe0)_1s;!CByzdO4$2W${}ZU&>%!T-Q{fla z2=He&aPMYf+a~(py4{ZM&Q830^yrecw%gy~(jV<uKRJk<qgSHy{F*`td%gyjnH^VW z!kf0YwW6b|3*$~F6h%QW7-aLa|4gHGq!oK}cB6U9fl+-L_<etY+c61cZU8>}ad^Ex zwr9nlxBC0x@py2#y&Ya3QSuPfcFO{-tVLdi7U_zF)025@-jgYV(|Z8tkC^w&!{_ry z51JIcl-e2BEPJ-vF2Q{8AUN`K-gn^Kpq0({jt^sdZV?8jZsWv_=P@Rz@PRZ8IZX>6 zxhc=_EDg#spByNqHX^Rs2WYd|@Phdzd~5##n=|z|R9uU{y>3)(*^e8;-Dv1JhBfIb z*!&v2nEx_@p%9}tME;YeckjOac%`O>J-<_54}-~s(TU%2`L}C$d)E<^=2tL4D)-z@ zqz6RQ7axJeoq-{hfpEey{>p6n+_r5n<(t^^_VRM*Ht2EshflCeQwm+KanZNm^jJ{4 zsTL)Ma#U+7VY&V#i%F4C%M1L<94R($_Kp0C-mwwXR@5z8`}D$b_~!gLQ1%*of4%B3 zoKq9H@bg#fqWmMbO(NljH_#KG|HXm~MZ|~a8=|Y!TzAJuu|;FT$7dJjSA|yN+_j6? zQ+xoL94)J>k+PY5mM5DImA%S5kvfLoA7G)-(ba+7hH(9#pYudNH>mY&4BWkm?6tWN zL?JoQi!Cj%+j|ylTv*9y4h2K-`F#X5gp==`LK>+Yo1V$X){;^bmy}@bx+fMLRtYLl z0t$pgpc7+Ge0JsxOAO4DtE-nioX6`$Z(lzfE#J4Gv9S^Bve&bi4wp`j(VNhw#~R*` z0;*SMp|0*tLTixG8{5)+bmAoY-&*u6Dyw#4M@1#0k}@I+V$^f_L^h?g4h#(u2grfZ zXhcCl0STWnMis4x9KUetnmczG(34L+9Zm+s&YZ#_B!n2C6sI{LPPsH{^0Zof-rS50 z1|y-)!xA@jQ}JO}7Yq8vTZa);g763Y5XgI^^OHnrNvJG@pCHCf3e+<x81+<E?FBxn z?9qcN1XKtJ%x+X4U8ms7#ysgmmtCYARkA7?V^mpeye;l+<FCa9B}yGLt{EL|4`Z9N zaI?m!NT?{;UU`hvWFii7Q`tQGz-{g9{7ENCGU>u4=6#V&^goM(4D-JN3;?fJci#?} RB{Kj3002ovPDHLkV1ntrJbM5D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_h.png b/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2a34b90711d66529432b9897371671550224f3ac GIT binary patch literal 1435 zcmV;M1!Ve(P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%JxN4CRCwCFR_kw5Wf*_nb7?!r@NC^| zm)WifOe*fi#ZmCWW{43(xM&uqK=6|v^_vOb{0mGBQ4@_ZqA`5Y53UGP9AkjVl&iqF zYz)_}w%z7vOS_)a)AJsm=X9NIDJCS2H+gc}_wxLn%kO#KmtDGe(G387C<-k9h@%{l z2+-wnDfKSbvLnvLVoEF)>yx<2m0G70Oe44Kpo9~NMij|30Sv<c^AW_BqYxf~n!vdM zz=Y)oH8~KTWO5GNKzM;KKPWeW@FY&4=qZ0l%w#6+7`isFb?3%62GqF;MN7sM(O#{t zXljrCH!y-CHW&{;92y*S|28t>fB)dYq4Krit0w}JvB^O2=!foAxumZ!FJBpmdY*gx zE2-M4R?<m$g5@a@se%j-4|j}@k8hd|g&cY|%Xltl&ByQk?U)GP-qhCI31gb-o3#nR zz13hmQ3blb;GRDB-F7vT5$H^@KEfe9f#PhSGGcJKL?RJCejWWk^sil5SP-+Qgyv6P z{3%dh)8MGDsRzLnU^K%lFASRq<_Zb))a;sgA|YoMGD5CcH~uC(BvL-StSk(tPoHsT zHO=Z$SFZf=VmiI&8ZytEn+w(4oV>Y}=I>1Z2`!%0Fcq7EbJJPSWf?XgA*%#iGM!3G z445eh%#>xB=R=FaDZHj(F^ZSTuV>G86b#Tb?eSoLzh6}~@#OiRyBlj8VgD-!;Pswf zc;~fuq4%Xeh~Cpd%>azfY5*&J7qYs}R819f#=kl(9K;g{xp<)<fz5%-&$PA<5a`zK zo+DnbcXl#zr*=9z-O>B<ThP8{0|eS%u#B2d&ZG4r?0UWzCJ=LyubiQJB;EPS$m=8= z%Tt4sr}yVG5C}v)9#4#vZrU7}@_4<;!QYR+`gHSJ@VJ{xQZaD)81$^)25l|tpkq}h zoWC{FEih(^qR1H|4&`-PI4F4{S#DPlo^Ot_BpbJ;Mw{*?;@%xSyGsBMfByxj3mR<O zvfcW>qk9kNn(jGu;lu{w0O=^`vv4dTnfa0d#wwQ;4kQD=bp8Am0Yv!h+fPe2{1pj9 zbCVA~|K^jWIUBRX=-B9%_5QBQO;S_JZnGJdehRtC^X$!gbbrgIycE8zt2@pJ7%u$7 zbUi0xxWpURudl7EsZZWZq~J#4X31HX;({CFqm53*IUxv`aY3|{5#CZ8EFZyO(0_aU z_8P1HkN}vkkR;(vh@hILA}D8r!NFe4n^srn9DCB^8AswxDT<;MbTITTo7EX^5$txm z!4}&fJ9;$uf!A9~jG37k$H#{by+@7VVfwMnW=rhdf8a}}vrZL6!SalPYPUNKS(Z2% z5eiL`r>4cM;_>z2!@CNAHk&>1Y**KhNCYN{l0n5@*mr7ZXdiv+8yc>*v^;fnV^>$m zhRP*TG^io4#l4hHzS!RviDWKK&^UhVxc~0_yuYgIv1n~wT?n5t;kNenFr7qejY4i{ zgu~%qT3g%3+>MPXWG2h$IdTjg5||{*CY4R`?NZ=}b)OapL!+UgA^zsxeINOJz9jZr zIg?Q(qB>cQo-SEWW9RPOBdCmJwXBF_0g1fCyyITOY7S-va$5IknWvrWI-|5%Lq*oz zN8Vw6s8%s&yg0mz;X*qEY656jejN=ki@hs-JrrIJWs77W9YzrAwML*AGK$-Mrqi5E zp%iGnG?Wz!-@3%hJY7<TwIyN5Scx$lDnyLZZ9ZtNQti{bH=atRl<MkgcwkHVpGG>J pwjSJ+(<AYCybtA(z3~4LU;sJ#b~a9bp<n<2002ovPDHLkV1nj-v~B<Z literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_n.png b/Templates/BaseGame/game/tools/gui/images/menubar/world-transform_n.png new file mode 100644 index 0000000000000000000000000000000000000000..0aa45e46c905335971a20312ffdce88d7a0bbcfb GIT binary patch literal 934 zcmV;X16lluP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#NJ&INRCwC#R$WL`K@^_3_wJhE!|sdw zW}2c8E~QfbRZEbCW$12#3L=tJFJW$xQV(IhNlz8@7FkFLB0XhU8G(rZ>GqKJ5V+-9 zMn)>^uDdh$-p*{Za@}^<yA=u1z{mXDGv}Lg=A1JZtE!6rb`#JRwEv|49;p<$kd>aH zYvZ|#Z6-kw+HYL%IPYw`lnzS_%P@Dv+xN~r7vIwSy@h#ztI@v6vg{;;IO|U~y@cr; zfc4XG00#5#xVI6|QczfA=CYVAo<7@bqLj2)t+^;ACFS7i>I!1{t|&75oCmlGFvQZo z8L1s$0#pNxVR>1m&eT+k$!s<wj0Gf$Vn~>c6-8mgO2BqbjCGST*YbN}R$PHDiN!!e z)2R$XXuI3(Mtyyck;P&OS?TxtLxgZ3w#5V3#t$kcVriphoVKT=w2GaaP||L(q@udo z8f34kL=E+I$YHmmU@#bR@6wkgMt+CumJ+fQ+I_$UHV`Gr0g7fFwRM@4&_)N4<z#1} z-Mg$I!EejUNDwfps5pisgM>aWF1}$7;?RGm(-Y$?^h4o4IWjynH8wgA5qmp3CkHW0 zwQb<}0J7O^C_5_?<yx(XQhE)R+2vB+|59-v#~sy$l#qQ#%a3gM(9`=6`PbIa{?g*m zy!>z(SZgS$aGW?3%UWp@vhW5P2n1S<Nk(+H^WFx5j|&SZZAUuldeFVTW;7U(&+Bcq z9V+V@d@=Be9a>8+>9kbE7<B{#LF$?Ju=`79SMBwBYcR&1x3e>d;U_l=Vm@?VS(b6c zCxw(@Ia(E-KkIum#7`{n^btU&7y1<}2>nnJ`+Yv&@XTx1N`$9G4kc_Yj{I|AwHK6` zcxf{wSj#_vz5}kwiDivD8Cj4AmC9;wEK55zgzq6`n)v~V)=i2mKmAdOaJL|eJrL!C zTset|Ts7{Ua<?+LK-mzuZVZ%-^G!sX0tH3ImqF&qtI?4~?sk#Og$ZR{Pa8BhDB3wS zKE_H}GB0OXW5GHp<nAp_WDG|+mm#qVLsU{;6fjqWxdK_EOo@`~G{Qn9p+{W%WCGT> zGDQ=8YLr}7;*~wUq^c&jR2>B*p`T|-M>YE64R6IC{&xWe0B3Vhs9TkFBme*a07*qo IM6N<$f?RQ{4*&oF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/move.png b/Templates/BaseGame/game/tools/gui/images/move.png new file mode 100644 index 0000000000000000000000000000000000000000..70f9cd5404b8a3978810b50a391c2550e2302bcc GIT binary patch literal 3232 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 SEq3bw0000<MNUMnLSTXziw^Pt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new-folder-btn_d.png b/Templates/BaseGame/game/tools/gui/images/new-folder-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a9417b4ffb41cd0ba6993cd45f2698840ca319 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xrg*wIhE&{2`t$$4J+o><XQR^rCZ%(03yweS;XXETH_u1O z100{2PB6&c_u)Bd%^q&}=f{^a#tD^zDH49-4F~Suw>MNt<(VTnFV=EO!|8AN@8c9C zKRg#tW7xRS@Px$92POa3+IkDH{rUZQ@*jtmiihH@Y=0JBXsDke_2KpJbg$SC#SMoW ol9=smcX>}^?3t35-oU`jATH(<bMzRGE6{xmp00i_>zopr0GfhcUjP6A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new-folder-btn_h.png b/Templates/BaseGame/game/tools/gui/images/new-folder-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7c424b7a8f21e9bccd91344b03dcdc54079c13 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XW_Y?dhE&{2`t$$4J+o@V(>q+{F={aeeRonj{Ebf(i3vC# zHy3vPbnaj%Tl)0fXW5PPn|KZ<9%J0VSYQAD$b^Z81!)y!PXn0rZtve)&A3QHk?qgV z)`bn7y}gddPOvWjoc}hGiIZ92<NNdepO^(7wZ@A`eQ0-L{uh{aVEy~~nxzNNGx9Na s@&5SoLhmM<LC`GenGzZ-42*0H`*+xvsU%;l0=kjG)78&qol`;+05&RJ6aWAK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new-folder-btn_n.png b/Templates/BaseGame/game/tools/gui/images/new-folder-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..68252b834ef1c274f9bc59c8b2f67a8996628a72 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XCV9FzhE&{2`t$$4J+o><>Zd0sclhkMaOFx$hyUl-*Vju% zIPiprhx42iR`-**a`oz2b|d{Jp2La97&kE1|NC=r!o_RX(smR+c4N}JZC~?)VUff^ zwm<*=N?*>`(c!VOva*<4|Kx;VhgFQ8f|WpfC*%G7^~=vR?63bHl*X_vV@bZmo|>PN ma+oDF&-VK8h%hxUFf*_%vG;ztN%kVpbqt=aelF{r5}E+sYF&T; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new_d.png b/Templates/BaseGame/game/tools/gui/images/new_d.png new file mode 100644 index 0000000000000000000000000000000000000000..bdfc9400d6cdba6cfa03ffbb988decd0c94904ef GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fh{`~)M&%CsO)y2Tb$f%;K>MXmFaL2_hxwpgI zc$^*BG&D3+Y$`r5EMZvB7#DX>BFQH}Du;oepa18*MyrVrpPZbW(8E1r_Uy;zEc2BX v#WFGdh%tOMDR4TE-t>gibB{1JFfcQ4Iq8;4aVT#C+REVR>gTe~DWM4fJ&8ck literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new_h.png b/Templates/BaseGame/game/tools/gui/images/new_h.png new file mode 100644 index 0000000000000000000000000000000000000000..1f9b722c1709566c6344b530f963a430bbc91e9b GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fhQW%(YxA**X&}wYtC@6hi;#OiPHi6OUQx)fX zxi}`x1KADRs@1{@)h2K|ux%*jHk!a4vA{3oM#&#ViN{QJ(?0#+Rb0}cF};DoF1%+~ v$`#e-)2cg))HgPK{Qv(y&lIKx24)7eUflw|mLHNpTNylE{an^LB{Ts5anD1? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/new_n.png b/Templates/BaseGame/game/tools/gui/images/new_n.png new file mode 100644 index 0000000000000000000000000000000000000000..06531327db1ffcac89d1141c455cfde03e997068 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GS)MMAAr-fhQW%*0{n!6=&}wYt*pPcW%&o*wYyu+}7Z)eL zoDGBKf$WCq)6K&Q)h2K|uzmRO!C(S+!~(w*i^@+b5|5dlJ$v?pS8+*)#`FdTyYQY} vDOXgRPpj@IQs3C{@&Et-JX4q&7?>H9ICb;qN8OSH+REVR>gTe~DWM4f=-fh< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/open-file_d.png b/Templates/BaseGame/game/tools/gui/images/open-file_d.png new file mode 100644 index 0000000000000000000000000000000000000000..00b709b86280b45802e9c56a69415bda94dbf41c GIT binary patch literal 481 zcmV<70UrK|P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzgGod|RCwCVQ$0(=P!zpM6N5x*W3jOX zMG#zEbQ7!4cF?7QKOtTG9r`cS$*s=1*A`qA2cd(Ll~$W-37U_leJ{`Vq7AWHDd^yZ z!wWa}ocr}2M+kx682A4K^7-v0dK@Z>k`zVp3!&9&)y~dNvw?#Ngh2&L*=&wft5t#! z3V<;tuImy_D>TFL0u#A9*$T=?&dkADIt?@!>h*e%$n!i<RTUzUh{<tW;1@p5>-p9E zvK&vr?ClL`ng+VAL%2OA5GD=^7(K@X)O7$O47P29Wm&-U{3ij1LF-3UY(od+I&3QM zKPY(+-&x?rG&`%nFWa+0AZ_6BlMj*afh0++e;5<6QaW1S>krXiBOPK!nuwiGFRs8r z(S(guWz=g>5rQ63&#z!Xxr69b9OUTaP*%6of#=7DK|go=3fOcni<?A@sbD<ZgOW)6 zn6};20b^9i?Oq%U;S1Gn)IgTU{do*C$L=t4nUTY51j}~IbWNGp7ju-}_y6oMy$LV? X`Dx(N@+Pid00000NkvXXu0mjf1B}$p literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/open-file_h.png b/Templates/BaseGame/game/tools/gui/images/open-file_h.png new file mode 100644 index 0000000000000000000000000000000000000000..daf4b14c9adb190c96fd1cd1dd9ad10397e84b49 GIT binary patch literal 451 zcmV;!0X+VRP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzWl2OqRCwCNQ#)&eP!v8IF-RP&V4$N} zyA+oUUF<*T81gTQOUCN%qEojn{siZmHB)~>mjoXep+535z2`zDAzWz5&;y6-)${ni z?;N?~@d(!#4_BISdYMkAcjS0g6y;VBgp1%T%RYl3cw`pfIL@!C>cO^c7>!1!?_s~+ zgJoIr1p?pqI}wJ%A&kdk*lxG5TrRsH)JfAc==FMEc>Y+DBox7iVHo$SszMlsFqups zO;b1(w{2UBf~CQFv)KUp;c$Suu7S`vOTc1Sgf)eEQ53M-?Ygd*%=3KbtnS?ykuxH| z0$2#|P18_E>-YP}AB)`(AzzR?I&J28o;er{-gRAv#bN<}SgiSMHZ$oMkvk$lxUwu$ zXBoy3k=Nw$iBfPJCqk&J)rxWzMUhfqd7kH(_w2gvx?3cK9mg?9l0-FN88#wlBa-}f twu$6JM8;&6W%(QK_SyFS```F2zyKx9$JX!qX3zit002ovPDHLkV1lsN%?SVi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/open-file_n.png b/Templates/BaseGame/game/tools/gui/images/open-file_n.png new file mode 100644 index 0000000000000000000000000000000000000000..8e84251c594ebe832a680416ca317aa7c7fdedc1 GIT binary patch literal 488 zcmV<E0T=#>P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzib+I4RCwC7Q%z68KoFgl*2dHzl?Xzj zF){JtMQ=tlp&ayri9ewn^mpjLP*2|QtapUyRpY^ggC{Yd6eW%JgF?6K?ixr@F~pCT zyl%6ddGlsx%VCV+H{kwMAe-G>Aj^TGC<##%KLs}$jmqiiiQ2bG1-c{x1y#-9a=DB> z&+8E(gk9IgnwF~v<0*x!<Bgt$#MBI|q*6eFp<1o>iuk?{@pv3Up^(XOTwig4tk<*4 z*+n^;gz4KG&@>HnU58+6R3Mlb1QjqkjtO4d0puaDZ5u4h0-opl1Sr1(jVjrO4xZ~! zQ}3H3`6CB`eL<u;b---<HVC*0JbGdj`3^{uMEi#k0V##U)!ps^vN2xf*VEEi<ZN<& z3HI|QtR+jsUV)OjeV!Irk6wp4m{aZ`JP`#sJU%e1-D<(}W6dD1Tg(A{5EodC#KCyD z2PGE!wrsPm143RdvvYCO9V6?fS*w67kFxU^1jlaC<RVQDju9-|Es`;%Uti2&df)rM epXn!f6JP*9apH9!>cP4I0000<MNUMnLSTX?4A%|- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/radioButton.png b/Templates/BaseGame/game/tools/gui/images/radioButton.png new file mode 100644 index 0000000000000000000000000000000000000000..d5ecc98534ef4cd9e716337a68ab54ca69b31687 GIT binary patch literal 843 zcmV-R1GM~!P)<h;3K|Lk000e1NJLTq000aC001-y1^@s6zFgTS0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!?MXyIRCwC#R6R&rQ562(yDvX#VqYQ- zno<&-VwajBVg+e!BjT32xJW4etnJv@(aBB~w1_y{4r!f)f*{nX#YxZ-V-U5F_|y8A z_wruPxo8?`Qc?;Xq>tR>=AQ4|ob#P8pJD^_s{YYn+X`<+M@Mz~c&=K;Qz*(E8MW<g zOLffBZgP5h`jC$l+qM~ekM<w>`}_6o?rt<RG)R0lo5k|-G8PvX8T^ovJ}V<5BYbme zXlO|9>gs}N8n6llnV<>*O><ynWd-x|^PChCkx1kP-P{+8J;ufPIc&=+*30{XX(H4T zf@Rt6jg5^9mCh4fhLJbG<&|3;V+><8dJA&R-;R$eGGkf!tG<HT&qJ#!TDhbxW<ya_ zYPe8#af~sH<%Bt=&gFJf&@8*$6k`lyIbo7{UR_<qEnSDuG?)cbF5dImM~V?<i?OAH zdrR~ND-4doaXC0nD3u0N$822o$iTVl(zt=*+uz&6=H?e<a|aMYg@dRq6(1+e4i69O zLKWD~WU#RC2Ct^3kjv#zSLcD>=Y!MXM00a9mX?-+Me!XU9TMmO<KyEvJ3B*teLWf* z8`0X@N{My9imyO*3#$3_^i+OdUtdQc5P;L^ye7<|_=M2V5(;5#Y)l4rcX!d-+Y67! zQ(1gW@x;UgQmGVnc6RWnr$_$dDvHlbbsL6302fY9zQZ8!@5EQ#;BJ8U1kuMge`4{) zVlfh%ARoGbNo=`@t#G&vKA+FMwzk%nOeW{5biNjzw*Cjsm;dj4)eP=NIp5!lPpB%K zE|;_%woJpwR~DZ|?I+*8M`!1K#N$tp-p*7OA3NXP{#PUt337~+qPe}EnIY$^Es9UQ zqKhSvA#c3`^OH`e$&_>DosVtZ@Ap$FaT%zoslnjjpmgt2@p02W^3Mn1a9GxQx2+A) zjt&eA3?S0pj;5w2c)ebBZcx)NwM+3mqZ;*SG%DRMa615p!$H4NE4}sKitmR20|2im V3rY;NUd;di002ovPDHLkV1nBllNbO1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/reset-icon_d.png b/Templates/BaseGame/game/tools/gui/images/reset-icon_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6c9c08a874ad308540605d5aa77e7861a4b09c57 GIT binary patch literal 418 zcmV;T0bTxyP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzL`g(JRCwBA{Qv(y1Eqk8feL^shOi)W z7#SJS<&g#clj;&iuv}tSA=Dx!Gy`$F1a3fZnGgfN$T|i_COaU1HW2@T;=fQAp}2|> zId&L9{)umMVqjri3*_;G1^@m&&hY2=5r$vi4=}81-v>499~5IXgoz=!`yP-bkJa6O zel2HMSd|83{X#MfOPD;mX3p^U&o(&!{fiEUA79on{Qv)nfrTxSA-pCLD9Q%J%<ymm zxdeuxf*=L_K*jY8$vq%JWgwPkP&Ls8dUZQPV&@egUl52n;Eupz2s2axKZAXw5s(%K zVqvI8VW1(GfwT}1b3)Z(#Lj=HKYxMggUhc&4gLp}V`BL6wGBxBMvftDQ4EX1-_XEg zL=s~H#?~3A94xvqVuw%)XGFp<ga5OTm`(m;qglwvZ~tkMiva=*0M>VS41NrT!T<mO M07*qoM6N<$f~9<_0RR91 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/reset-icon_h.png b/Templates/BaseGame/game/tools/gui/images/reset-icon_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c0dc85cd6e75fef0eaaebc14b89a4c0a5032df17 GIT binary patch literal 434 zcmV;j0ZsmiP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzR7pfZRCwBA{Qv(y1Eqk8feL^H#$aS* zM3+Mr!0B9&Pq4WJq?t)MIK2>R2@{%uxLpD>K+Zivh?R$b9Rnki9gsg8i2p$GU#N>P zT*U|rSfF<pl>$?o7@1kt0(tykr~dtO9Egwn{r&U6qwUM~K@Ix{#c=N+8^WX<l5r2n zlE>=zzrUB?TR$fa$oz$57-pD!efGfo|G&T6VEkX7-gW%_`F$Nw<tIjF)<}7;*hHW( z8xS+2gcHalsB0LZ3iyGF>y<+?L4wLaEYB;it_}3+cBP=SD?q*=5Octi2UstfA<R$( z{9>j~MnGB|h=rjVg@J}#2GT-6%n4Ns*2{vH7XCr~{O{H2eb=D||AWdg{rmN!4M_h+ zk0JCZhDG6TXy7p-i81~A`{xW)?k}=I$gx8xbu%Jin8E+iy+a86$3c^12+ll2-CRsb cxdISi0O($mjw5$3PXGV_07*qoM6N<$f*3r&J^%m! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/reset-icon_n.png b/Templates/BaseGame/game/tools/gui/images/reset-icon_n.png new file mode 100644 index 0000000000000000000000000000000000000000..f342696705fc99219d27d8acd44f2f42d3494df0 GIT binary patch literal 431 zcmV;g0Z{&lP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQAtEWRCwBA{Qv(y1Eqk8feL^H#$aS* zM3+Mr!0B9&Pq4WJq?yUp)wK|62@{%uxLpD>z}nhch>MGB9nfWVK>lnX{sYB-p)SI3 z6(cNQftnbdot>SSn3&cAnfxHJzkmN8|M&0Tk>9_6A2@R4$UdlH|DYJ|9b`j5UbqLO z<*_*#XyEd_d-tXRnZJ+>!wi$xuV0%3ZQcgsfBEvI<JYfW>wpSBF*7qq+S%D90)^Rt zm>DIUKqf(5!w6Nt4^&+5=H><xR0d*sDJdy!pjWrMxVT&a@&$pI1C~6%deIDFhAQCK z(a|vi(&9iY4Am$MG~_an76M{Us9LaI7PPeR59;TCSFT*S4mJ26RE`N4z->VKH+l@A zM=>l4e?tS05lM^*7`SJka(|HxLXI6mshbfA!wmk9?j1tlKMtBCLvZFH>gHlf$`ybB Z0{~<TlqOUTj-3Di002ovPDHLkV1fl3v=IOR literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/retarget-btn_d.png b/Templates/BaseGame/game/tools/gui/images/retarget-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a8854b3f89c8d50cafe18656055ef6866d04230a GIT binary patch literal 594 zcmV-Y0<HatP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz^hrcPRCwB?lRZyVK@^7HnO)crE79K| zg}Sa7t;Ap?!3r%fhHTtyprFB$0wK|wU}I=4423ZP8e0mr-nEg&!ao3u`*pwO&Yc<N zgJFSP5))7Exw*OL%rob_bA~9T@IRI}ugU6;vLIp~^q4(3Ar#*~&60rtDI|Vuy*L*z zK;uFPP)hN*x-+l>YLle`OF7asv(6aD!9fG%av7aYfFOvFWtq<>eHCyn(CWlG;&!dq zYgk=bMifO@SeQrgP6^?mFsZ{jp177AhnNMWXItv?#uKv~Qc5h&&!N#c>??Qp>Me|p z&G{ZdbZfSSI8JfY4Dh!VSzmek9yf1~+q|ji85Hk6fFeUkoBMb*(K2FU@8ff=op3FH zML23lRxB%=%Hw6_HNNeAMd|)S&|DsWf)a|?f1y^Zqf)6NNthN`IsoBKy>obSgkJp& z#e0*8S*pPT2_M0m)pyw4-A20`X+Vrri|qi4BhxfL0B*PlcqWm{7i_OW4IyL_Q%`5m z(CsqDK(&x=4p<W#>tD_d&gmdI7HHubx}m8iha?T{v5s6RIKc0po1`~!?(@c@4{CjF z!s_C!)n%t~rKE;<5eL{=T`o?jw|1L>VnBj%uM5|^oOSchyU~bTb{ogWm(;}z)prjH gqn88z)s6)i09*wnMNDZV1poj507*qoM6N<$f^z5v`~Uy| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/retarget-btn_h.png b/Templates/BaseGame/game/tools/gui/images/retarget-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..1cc9a078d40978df899772aff01e318e09f8eefb GIT binary patch literal 590 zcmV-U0<ryxP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz@JU2LRCwB?Q$0^qK@>eR`(Q(?M1O-6 z;<9SA5<en|R2vg8glt?16jqiNh>6w&8)IuCS{M_gv86!jT^emH_ye%GU+-()yqR%6 z_ALwRHhS`Ia`W!YnS0NiA$Oi^DGh+w4<>c!B_M?2`{q2s8JK;Z6cT%%9t{*s2q8cz z#l5Ak7zV1O0!uiOB(=d9$Khce<#HMAwh!MAk*29#3)kc+c$^C~+mWugQ){&<)>fY* z3`4A}ETb@0L~ta`+EHDP-AE@qWUBO0ecrflwnIvZr^}0|*N+BT9X&sZ@rlK3Mi4#P z?uR)-qbR{~!^iJtXk+!&8(h9}&90kyP(tDAO(-&gr12Avr<+Dh?7V*nDTEsVEW~ju zv|?G|l!t}NGwkksL-EEf(42<@zlg%cAE;Jqs8p5^$4m<>9RcADeR9yf!c6@Xh3j__ zu|yLK#C!}d)?Q<Kdkd{js0kt@T5Lz4I8wvbHGuVmrsJtZ&db|bg&IM?BxYtysOxbV zW1w9jJ;@r_SpRa$I6WgdF3|i1bOO^&4sjCLYaKh6cLcxo-;oo?x%ATQw`%?6wACd; zv%`9IrKBdwMEnc-bvf(M46PPJ=B@z?XS#5GN`IJtSs0Cmb}(F*zBiB`cir~4^)I0K cPy7*J0R3Sj+oItGwEzGB07*qoM6N<$f(^d|8UO$Q literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/retarget-btn_i.png b/Templates/BaseGame/game/tools/gui/images/retarget-btn_i.png new file mode 100644 index 0000000000000000000000000000000000000000..60f8b3d4a16752b05a2e4e980090da33a013e22f GIT binary patch literal 499 zcmV<P0Sx|$P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzl}SWFRCwB?Q$32pP#B!VsGzksurd%> z(%I@2#KH>*78X`w6Vuvy0y}FD;0c7J^#qF8`2U-%^SLjZbxpDCzy}{M@6F7c??rPQ z#}2~~gr;dweH>gL4u`YX_0?(xBM1WY`~4puNW!|`@6dG}uInNRIyYV}7hJDbDfa92 z8qH=CyWI|l!vVM3Es?<BM{ZQ`eP0xh$0OS9HcZpRVzCghhvk4xp-}iHpz3zJ70bNN zU@(xw#C$%NBFg3R$0IN4TdkJl^1w_#pU>EAHbTPccDpE*O0uz1slc)<$zi!%qF5|~ zB)wiw#G)&ZfSc3=8jS{~(<yA*Mk<v;CX*3G&+|~P*FjMRECi?1NtEO1lbwedC<%DD z)9GL`nMhtFP{A5%T(9%zhIBeD&Ilf=R;%Kc{Z|GsIHu8P^lLnE!hAjtIwctiNS@7R zp9JIaIC^1fJhfWQW)4c7*zfm|s&Xv^?;b}ymn2toE|25&ZL^dDt;UOMs-h6zIY`PS pj$AJH{L$Y1eSd+Df8tYs0RSN*9synjGDZLZ002ovPDHLkV1k7b-gy83 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/retarget-btn_n.png b/Templates/BaseGame/game/tools/gui/images/retarget-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..2a25e1288bc74c4349034723b1480532ffc96b82 GIT binary patch literal 498 zcmV<O0S*3%P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzlu1NERCwB?Q#-E0Fc6%C5G1I%0ToCz zbhP9Oh=L0sQBY7Jnm|j-3FxRfK(0XAoFE9%@Jsl=%z{JULGkoPT4`mkXUDU)X502H z0tijhpz<WRJ|2&?_x0s+3FCUbVlWuIY><R;xm=*@I)=j`zCmZ<@p#1Pbdq4cTCLD( zwXof8vET1;KA)wQV%`Z1Zn99pbzSKg1OYmo4oamGeBbAm8A%SA<n#F_0lRKC8<EWG zj7B3lOn9CrK~$^NAo5G{cDpUUP?3H(9I#%mg@n=T^-wGprLb13p<FJD4~xYDg+c)& z>G%86EWQE>SfnP<Y&PLI4l0!jQmGV7)0B?$`5cW#18hozg&+(=>6~<*>^w|ENx;M1 zZWpuJO#C9j!&n$<T<`N|K{}llWdsjdmL+PLKRkfJF-<0uU-L~8=5jeuDalAc@=PXk zCzwvB@od$2>h=1KKB(@*ZnulOD%C>p>2)Q2`KF4>)$4kB+YHr#Qj@_o6|sq*6nyg~ oiEK7||7f3n-(R5PpZF1A05iKC{Q3yWX8-^I07*qoM6N<$f*R)EcK`qY literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/rl-loadingbar.png b/Templates/BaseGame/game/tools/gui/images/rl-loadingbar.png new file mode 100644 index 0000000000000000000000000000000000000000..7116eec14c3b6f6e547c1fe3939a659e1dee4131 GIT binary patch literal 630 zcmV-+0*U>JP)<h;3K|Lk000e1NJLTq002e+000>X1^@s6jP_Wj0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!7)eAyRCwC#n7>ZLFc8MiZsJCvjVcDj zfau1A7=eWm9)hPp2nL>j0S2Ce7ePqC$b{6b6RN5T^&cfkZO)C;q?zrClQ=bsT}9s) z{d_*#VG00$|4|L5lzlGj`t<;Bw|JeX`{O3NdKuZ&uV_Bub))W%-fen#PH-#v2;t@) z)EoYy-`Z(f_sBJW#-}hHw6V2M0n9GcJRj0o*3a)YBiGbE+Rt*h0W(hl;%U(@w{1mP zQ~QwsqvU$QXg?BQB#z+s7pezYKXEXXlF~F~oDi5%4<On$sUAC+u5p$r!%u+b78NjN zDh;|>f84k4p}}hpJqS_~sxf**^Gq4{?0Zc5PHPNzHkH;HG9^@F%IWPO)mQaXA{hhF z#f{f`(kAoiQmHZLAi@V+Mh~&K0by%m*`d%>@(r&G8ejlAxMExa_d;wx<DTQpvVNf{ z(<z?GIyAsA-p0+HTD)`?4z|8VQ~PK?fDZ~8!!f<(a!1Pe+yVS~X{qKkGKOP8^hEKY z#}f|L(~>6l=5)YtPnP<L1CONCmQ-p?f-lQ~n+&fv?1K?uvfM8*P6P}`FrMHLM9P5C zX9Jcm=BBMG|1Ggr7yruh*|R~EEZVanLq95DFwKLAK~0!Vn6RHQH4z&?Rj_{?j0lU7 z_XGQ2a!o(br)))~#-s>lwH~;rF?l%n5atGqKEJGjNgaSGG-pHW^1lKM07t^s_00l+ QTmS$707*qoM6N<$g7D}XtN;K2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-all_d.png b/Templates/BaseGame/game/tools/gui/images/save-all_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c2c272de5d5b2f927fdc3972367faedeb27e0129 GIT binary patch literal 390 zcmV;10eSw3P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzC`m*?RCwBA{Qv(y1Eqiko1lwnz<-Jj z0Rde-b%x8AZZhmSy@3ITMGSY}Tx3ud(Pv;p_715oU={hv@cRurrb|e1fxMs^!<{z| zAub`;1#)sy438c?L%4(-7ySME7c9pFQ}p-mKY}jM(9i(WzkdD((|@3P;D-GA^^2e( zAQwO^gt*{0R1d-ssOfia-N$JNx(i^gXF+)9F9SCZ7ejh<Ax=ZkUGNi1!wmtM&cnkE z#<&2w3;zB=bIETa!UP6jF8KZXH@bHqrhfeR8LLKqeqMyPpe_I=2SyY_e*OfS&In<D zVSd4|efxHX3l}aheE9HzVQSk#xb4U;K+k{|fQA^but2!7va(>!FpO+FvI}4Umh1(9 k?EOG&KunmR8v+nu02K|kLT{VwL;wH)07*qoM6N<$f^o*6$^ZZW literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-all_h.png b/Templates/BaseGame/game/tools/gui/images/save-all_h.png new file mode 100644 index 0000000000000000000000000000000000000000..220cc9313d7f4993637780a5a566ef5ebf780483 GIT binary patch literal 360 zcmV-u0hj)XP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz3Q0skRCwB)l+O-CFc8MarilZQz2o2} zaoJbkVZ4xsakH=B;wD@?fH+7Htaj7g(dz#3C8N<XUw<QQwRK$qD~h81#1~~*rjesW z5Cl*Z1^wW#a9arKx(3hl*t^C_{w2_L9fo0$N&X?QZ5yhpdXj`;c#6Pr9B7*6O>%}{ zp66HoLLYcSCExeiIF2J5mu1<T@Wdxk0=yuu*_o7OsWcG@Fg@WDhG9S)$4V2C0Fw~z zZki?wl|%xG<d=UUwlz(Y+zwUOb%%wPWl3$({h=i3`ySFXg*?xpZCgl^WS7ST*d*U} z;Ej;y32<GP74s1L#{^t-+h4cwzQyg=qc53=_sJPO1Q-Bdn7YI~?m<rg0000<MNUMn GLSTXbb(@?3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-all_i.png b/Templates/BaseGame/game/tools/gui/images/save-all_i.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc06998f0b436969f11d891f32a1127043e6a25 GIT binary patch literal 372 zcmV-)0gL{LP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7D+@wRCwB)lrgG=Fc5~vC19agFNlSm z;4M7Kg5Wv!4qo8h!p<|;*on5=2x1|qsO#jBWlYTDxj#g3lKJPGki^)w4XC0hdcp<e zdH!T)6JZ!aS(doqG;uEkP1Aty`~2S!Bv%Pc(}ZyxRgx<NuIoZw*F4TB0UC(KIt4+% z38rcK#Mrk4%d&jtZ}_2szX(u*U|rXv2}RQh31~r@b1<o@N^2q$P<kc;mSsVbBw7=h zK-pMyw{4pbon!)q<Ue~Nw>8hR+77Dk`_n|nag?_3q-sfqVSp^lpePFHx(?DbJ<L-A zYVz7o;GMDG8{l~!PZmS&pAt}$pZyX(_6m0lv~3G<93K{)Ri`rK_v9PB1sDKcy~dG2 S$_)Jg0000<MNUMnLSTZ<pq=9Y literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-all_n.png b/Templates/BaseGame/game/tools/gui/images/save-all_n.png new file mode 100644 index 0000000000000000000000000000000000000000..36f0d15538068905565b8542283aef543e7bf3fa GIT binary patch literal 395 zcmV;60d)R}P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzElET{RCwBA{Qv(y12S+i4L}tj0QQ{T zz{rA4LRU|n;qs-MAO%=80xe>=`{p8pvWPyI&j@n~sV-m@`N{D64LhbwNO6I@pc=!S zHxD5$aW?TM%LQ_BQVfqCJwv#J92flk`xh?H1S0<a{YTIR8X6j4`q$6jVEPY|9<U+5 ze*Ge72*?Ex3n4D}jiiSO+4Q@&?&CBB-37?5XMvf{&BMiz9$kpj5Of#(L~;oW%yb?e zZZO6L&|UEN51LDU6A>mb0CU0b-@nnl12OgE$In<b^7Hc|yajauD1D$9^7ALqbg0l5 z<`)dxw{K^-aNz>OhYueZrnW6a@jtQ)kPW#2G{k^~1(E|~Wo5ydVHn;2$S!~ZP_h>Q p())qffY_{$VF-G8LJj~3FaYItx#XeXP1FDY002ovPDHLkV1myzq~!nr literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-as_d.png b/Templates/BaseGame/game/tools/gui/images/save-as_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a1905924d12b6c2c018890a9a02bf3f0f19bf70a GIT binary patch literal 508 zcmV<Y0R#StP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzo=HSORCwBAWFQ|f!Wc~CX#PKOqVfN! zQ-gtQRv>03)ey#&D_0sbu(R)FU}atXKO-X*D8|W5ssZfm?0Y_Z{>(4)@FBy4Q)j)N zF*B`WA=ZGG+Zl}i32oVP<dGl$b#``!{cqng+<x@1di9e>FPMonK$MeV5AW|w{QsDK zGVDF}i(&i8ll9wfTwe{8|3QoaCjSmFeE7GA;s3{t4FA_PF#NYMSFH!iX#%kf5DPF8 z;{t|14E&$JFfi;n$G|XW7emAL6AY_>@<0)W&p`a0unTqrU9f@S|C$<x{~qQHFc-*z zTmZ!ExRMEm0jq%qxSLU8Ko!G(H&b#9*n{u_F$TZ_mht&E1|@NBhO_WMJj=i^dnZG~ zj*}RH_#24-VRHr3|FhE<{XeP(j>ffh4F5eXi1z{$I796zQ36u241dBIZr^8Mn75mu z4k;Qz_$Ltm2W2*BrbEXJOzuX~i~es-`u}vQ`-vz=<#?c&IxHc9G8ohV5az?dW?*Jy yWIP1a8~|j?0kJ#~OF$K|K`~6R7&bOQfB^uHXvnIqo~cIw0000<MNUMnLSTYj>D=D{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-as_h.png b/Templates/BaseGame/game/tools/gui/images/save-as_h.png new file mode 100644 index 0000000000000000000000000000000000000000..e36c98a3bc9e30f4733a03481ebeacbb0fbc2bfa GIT binary patch literal 477 zcmV<30V4j1P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUze@R3^RCwB?lRs<1Kor2=T~gDcG=xCu z)Pl2Hc0WL;4jo(yUHS=h2o%(>6AN9O1iNG~R;;=R5&wZAIAuse+qAXmJ2^qbVzdW8 z?&ZS$^4`lGz<z_&>vgTsXxv~NLqvqqeFTPKXo4U-Q%WDp<?=nQoiLHGk3f>7s_VLP z7>3a8cCUQjf0|CGju=Id%jL8n2&$NY?0FtomIYYWL#<YuvvNojK|CI>Vk|3)0+Y!E zOw%khn@t1PTn33Mz)WB~9z(C!D|R}a60UiOkGzRd1*6djTCG-bFc_5hX2v`6O%4=z zT=0ebkX?e|aJUrUyj8H=^j`rRbXN(x1glI)Hk(Z*5{cKSJ=km_%Y<YynS!dS2MLyv zN~My-`8JhGf#W#PZnyIrzcD~Ugk6!NEr?Xt_55r$1KYM=kh^}rzdA^a4|bTih+Vc% z&U!2XA;e6l)7SWLfm|Zz$SJ?-F`fnE?2lMwjXmZFyyn#x7QAQD+WT!|@hiXpH^&6( TZEtk^00000NkvXXu0mjfU>?x_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-as_i.png b/Templates/BaseGame/game/tools/gui/images/save-as_i.png new file mode 100644 index 0000000000000000000000000000000000000000..1f52b6499cd3fd876da866ac1649bbc0cb8bf7b8 GIT binary patch literal 456 zcmV;(0XP1MP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzYDq*vRCwB?lQB<&KorN1<E)UFFglT_ zF>&c+w|)S32NouW`YB9GKZG0Kpw-3soEsM>HW?sofB|TrwSrRdow+s)HUYfkA0F@J z{ocE~cY<LUsKF8jgTbXB2&blLKEeMt6K(>~G_8j*eg?=y*Y#WQ$smH807OxwX__io zmZ2~V?^RX3fY<@41L*hry`m^6bfDyUj%KqNn$PEV@C_i2NgY6^)1iySLXjj1B}sx@ z*EKxPdxZFgoe8M}09%1hSar6IQ4j<Z@V$dRKwqRT06Iaw?~md*9)oYi#UKT430Q8s z`0I)r16UINFQ5u(4cI~$;H)iR2kCaZhAhji0KeO?ZTlPmZ)_4R%R-Lh45KJ|s9a;l zhpOB&4kf0P4&gnpA$kGb0AS3kFF9Zh%K2ma<f__-1g7s~I-Op_D8I7Kc*2?ks!Q5u yld>$Y0Kl9g-}AP@hM(CHPh#=oO7hPB5nuprVGzR5U%#*b0000<MNUMnLSTX_Xu|{m literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-as_n.png b/Templates/BaseGame/game/tools/gui/images/save-as_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e60ceaef49bff551aff713413ea340916cbca021 GIT binary patch literal 512 zcmV+b0{{JqP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzqDe$SRCwB?lFutdQ546&@0oE;VnlwF zpAoV(el2Wl3>#UHQc_C!1FR7K1!b~O7PA|JC}zkL3C%2KGo?`$!$eq2(I~&)aUR!? z#n245K6Tzb_kB9&+zTYP6q=f{k;Bo&aVleCR5cla%jL2`h%K1SZW<aI<UNZaQj-w~ zAv7P672z;Wf``-HWo6ZzGn*0-TzO&BvsbmfNQX5f1h%7597j$^-Di<Yqn(sQ1i2Py z8Bam0u0CMv;1OQGf5LMVa&!J(@~fau-@&!6fo@hnOOv4H#>xrK)i6a&mK<;bf@RR+ zIH<h|AA({j5Jo${3pU>c3uB<R#(xOhEU-8HM=%0vt^bQa`)VOkfnspsL3w^U0(gcs zb_-|$V0HtO>we60o_7#q?wBV=k5`3Cff*<5SG`?hvbXRq(1y#om_9uB;5ZD-ZDQQ( z`>ep=fg#3tnC3W`V6QE7(%Jx>JM6yRmWqDftp4_pEK*>Q`9quzMNxM7=T818VM>_- zc_>x#aX9aXIL8=lnj}RI?NRa~=ODi2<E@uh0R{l)QO%965zAo!0000<MNUMnLSTYL Co7`Fe literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-icon_d.png b/Templates/BaseGame/game/tools/gui/images/save-icon_d.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb28701fe3506160e0ee0882dea910b2139b4af GIT binary patch literal 335 zcmV-V0kHmwP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy@kvBMRCwBA{Qv(y1EqikM7Wp+{HNFu z5YW|AXSjUnCc~c78;H^fw3y-Un~My}BKizWFu}in|B>th4Gj%2jbzAQk_}N3(WKlE zm>z^7e@OQZOb>=j?%ujjvP)Qy1-N;*7}BE)$v5N=Fm`x&xWSlM@CO>Ba6|r)5wIAE z?Jso<fjI5s$InD*<mczbG~^GGOLm`H&mb)=%^)Wy$8hxMQHD>SJ~4dw@Bxln%O;|z zN4Vq<P>>N}6UYErSy>DNKp5Touyk_a=g(g#2@Yg{jEoG10dPZrYGLXbLFEZBM+yMh h`+?YioPY%gFaYack3DHBh6Vrt002ovPDHLkV1n#hh;je` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-icon_h.png b/Templates/BaseGame/game/tools/gui/images/save-icon_h.png new file mode 100644 index 0000000000000000000000000000000000000000..3803ec01ebdc776b93432c2833e2e946bab524ce GIT binary patch literal 299 zcmV+`0o4A9P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy%}GQ-RCwBAbaZrl#y|nsw{M>m3y7hu zt<7-t>Q#^cQ5v0`oEYxkzt5nks0ijWfr!6<|B~zi4Gj%An`}em<m4zf1X&OD4Y_mY z4q1k<zy!Ftxf#;Z(#SXD-@kthJUl#LOe_HD!EgyBfcl1f`t*q?O?-TO_+7Gh?_LIJ zX=!j`I(qad*x(NzKEQEvb2Em^v3dt&fUK-6h5;aqO+6@`T>bg;CyE5f02vt>3<Ho2 xK~~QQEDAs#Tnoy~<Q4^t|NsAIpcDWE7yxB)kg7eJe_#Lr002ovPDHLkV1l6)cAWqK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-icon_i.png b/Templates/BaseGame/game/tools/gui/images/save-icon_i.png new file mode 100644 index 0000000000000000000000000000000000000000..7da2c6fdf212a1e777b72ddf361ea1839060a5d7 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GXFXjULn>}1m6Vj^BrITfb9?*xM%E)2u3bypQT0{Jqd}wB z&(DwN`}_Op0U;qQpJzH8xxc^Ov#hbLjctzaHpQZ%qAAi%YubOc-Pu{(uHzh$z;x%% z9iGh0%qP;H-ru)plarG(P!ntTlCYyi$f4HRuJ)J96g4|LyFYq2cbDroZ`opEVJK@` zC6e=@onQXJj-5Lt4?m6A%WUKj7AAI>LEzXjxhZ~efByUwc4bOw=;`5UV?5EOusWSZ uj8(?Lim^lWgf5rF|NsBbJ7_X+7%({dP1yVJ8=o=IV+@|IelF{r5}E*bxO3$I literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/save-icon_n.png b/Templates/BaseGame/game/tools/gui/images/save-icon_n.png new file mode 100644 index 0000000000000000000000000000000000000000..9c6a959a7a4eb8506a2ca5530b2b0fae99b4a837 GIT binary patch literal 338 zcmV-Y0j>UtP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy^hrcPRCwBAbT;w-&p-j#b9w_K3y7hs zr_ONs(oK*6Q5s!L0~qeUxyYa_q7UXXfr!6<|B>th4Gj%A8*0d3k_}N3(WKlEWIYf= z{*dk+WIZS@xqItASuSCL32^gpF{DQql5fZ#VC?YlaDy?i;15!af(`jcM!+Hi7II?; ziy;uFef;>DC{6tQyqJdkL2}9NQ|lR|rKK6<<m4ER9zDwN>C-2M4<A0jackK`6!i#~ z`~eCwA_PGO$jZuM7y!c9ybnqz7k>Wyg_3wc2FS?BU>E>51gI8SJtMFv5CGEqf!KiD kqJZ)Le~O9|FaQWJ0DF~?r)CvNUH||907*qoM6N<$f~MVvs{jB1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/scrollBar.png b/Templates/BaseGame/game/tools/gui/images/scrollBar.png new file mode 100644 index 0000000000000000000000000000000000000000..e8c34dc857ea46068c4058709b4c60ee02cc1f20 GIT binary patch literal 3332 zcmV+f4g2zmP)<h;3K|Lk000e1NJLTq001fg005#01^@s6Hy~f`00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf02y>eSaefwW^{L9 za%BKPWN%_+AW3auXJt}lVPtu6$z?nM01R|VL_t(|UhQ3Jj1<Qeem%=B3+%zQ+{RM4 z9V{Y1!s1*4Mo8um{6NS739y6^e~=tv42U1V55lqe3xOE^07;Ml8L$v05;x-JhlvC% zU~>q_F05fsc6Mev?|Vy)dS<%1d(NekdD>E`yQ;e1ysEDE^{cOX4U>rfK727AJa}OC z?%kULfdF;r(1G&u@+9e`wY8N3T>kGncj&~46U6OZf90@Y!)V2d6;xDQOx0CYR905z z+D28VuCAuCUcCr6GVAK<%!Y;rv$?t14243blZ3-z6TPr$(<bxCkt2?ERUzta+qO;G zRaI4aYNIMieQp=<{L^A8K6&zl=FOW&@0uE^sj)$uP>y={@83`MR?;qMMb%v~6!_O` z6Ux&eDJ!a!tgWkKt5wp4i<czFu`0`$qG!&Wp`AN-5^^_zGCaPirly8=@7Y7^)~%Bq z$2gC2H+k{m1>L)MkC5vm&Z1|}o~65Y?@E2-ZpyxU_l_DGn`rm$J#_WzRcVJD$2gC2 zCxLRcwY5?OxlY+AMbDo<FN3~g$Bs00>Qu?ylnsYl#rhX6T%aypy3ndst0c!U&ZAr> zN=ZzOefso~DN?6w2X&EPFi1m&3?a;UUAuOrVxIH*_wP?Ub!l%T-D(&#XU-f+ef##M zo;`Zdgb5QOmdP*3w{4GythBULQht8Eq|Ti?Q%g&WZT*Nv3knM4M*aHr6D1+Yag1aq z1}kZB!6zd|NGjp_O-)Ubt8>BTp#6>6dQc1rN<x&fI2S7^KlNd@Od~6Y)XcpS3Wp_E z=iDD9w@0n5JZVJ-4jf2Fj~<m?7%*UfHlZB#h7KL-O(RV$-}rhXhn_55m_tuqMtu%F zX{?m=^rWr_Px9u?8%a&<*Ws;@t8+R}<E7WEbs~zwg~InffBrn;HsL;TjKt_a?%Mn| zKxfaLr|*v)mvs8{zv!38k0n><P>y=B0vFoY*eFT;806|)ERK!%dbrTa%1TK9Bjd-9 zr^}Zwi-N<44;PRUD|p8mJa{mjI(14AQ>RXy1kGcDhbtX7ZX8XXJXzkw;#?xT(6?{j z3J3uh>Ba9w{MtcKu%x6!T&t}`aS|7W2Z3^ccTP&GivWbIUcH(oO`0Ui9XxnYavbA4 z%5?&-1*M?e%a<=BO5(h$uP-kz7Yjmkmb6|ZFE2pFMP2CIHEU?vv}sa=9LG41awk#B zq7Ia{QxaviS3x(c<TmI-p|D_1ykZ00uU)%VavbA4%H0IY0gvjG)GDg1>2?E@105;E zR9{~&)<mw5lvnfwT<RoAPft4Z)U8{$C?u6bPhPsaJ?a?Ou3a<nQinOsP4F(YaN$Dg z$L~_-&z+-v`}PrHLvDiD79O&}{7#m1>}l)PttOOhZ4I|2jx8a+gcuZitEhD4$`!Ms zqC#Rpix)38j~_p7vL`X?Id;V^)8uz~lf9ey`0?YYbl|`NbLrBhsl}e~gpM6MmSaf# z4vshyr^beQs(JmIo;`aOl}3*qEj}EA0K<$KpHnmYb-2Cy`a0>A>greGimhql#EIf{ zQjw&e`c!V3Oxq|YTv72CD&=tqUmt1-QPA>^J$LS0?e%#WP%j>b2t4qxk)&k#^5x0s z71t(8Q9LZ8M*WU3_*)q908x1nc$Lp*%%DMo2E{4rrM;IrQC<JlSIcP3n9&??=u5pg zDh$O{a=O^|?c1rme2Tcil9E#C5tY0s;iZmm#;AW@mp5$KU~b&FQKlw93`};whfPiI zsGj4SRzXB$^XAR8iPNkvzMvyV4)avjhR}m8LC!<`c2LjLRdhS}`;{wK(up5_NP5Ee zP1olX_3|VqQYUB8M6BhdZqA}!tdvNd+=Oqn*8kOnpTnbxJSbl3=J2Q&D<x7VP*hYz zH*VaZLXK?~cIwPAPG0uwQbgT3To}91b?eqGLHv;J-MiC|Kc1$Jg@qE!EGjCNb<MJ} zo)YY`ri&LZO8hew@l622Hxqu~A=804(!of*fr1=q4kO+!F%A2~kre5{WZNO6<8=Ml zGO5p!10|_EICgDw7R^~S-4;mq^?Wl%eKX-3Ovv)O6cp*rA-oKE=ck`!%@gbPZi1yA ztdWlV^iv7xBr4#K8XV3}SkV#&AAjDmMaW!Gz)kRjz`gsH7aYX%Br43W;)UIStnMa~ z#DzY5sO69T^7EqzpH%TjQ9l)NRP@oKN47t}dP6IJw6?a6?lHIT@BjEGuNMDDNCs)+ zk76y|E@7=6irSS~6;&Kjtgck?n$xabf2G^EZ_BDlTKS`jd~MCaR0P2Q+&jDSSVbFn z`KIRAUs=grbp85u>4khIDr8hqD>W0WMK?v!wXo*Sox6w^%mnQ(TC_-zKFZS8^{EVS zw8ffyettoOj)7h%<kypc5s)$`{%9iij`K_)6oB~vn#QF|mt3zE4u`0)V*!2h&7Wxc z^yxwu-oJmptmC5$DIKC^Kq$I~+f5V$0!-31OwX7(bEXUzSyW^8gBJ#b+dWXO_4TkU zCUiGZ3<#|9VbKA~&YCq#7RJDgNMd@ozhy$(3{++IFT4nr(9Q<bm$$HFgxeWxg@k`} z2d4VgF<%MOk(V6{peSLlNHMc;lJ@n!;g9CfljV{4;Lwv5HK`pr8AVSz)TE%G9eM&< z0%`(!0*X?8K5Z`44jn;JpeG%&vO`ZmOF&IPPe4)d{}}}(@G9`vKu<tXZlFXd1GEH- z13h8d0-6G<^0NEJ&8diIvQq@82`&(GB+yiBo!G8N_W~^eH32;VMFCC0s%h(bFTcjA zjzUO4OF&KN4G4w}P*ujz6K(?3<b~wg2J_kw65Jb56VMY-6wnk<RYuVh6!1cFMLmU( zFjO!^fSv$MfTq0iN4@l_EqVfKvg0({T~MJXpe3LtDA`8cXvv``FFnxqpybe#-c|;v zmgkrYiaF0tKpi^ERQJ7m_sYAvohX`+Q<up8<5XnFjvdkbQN<&Dp98NhS+c}DcI=qR zlug^@3Arb<781X15&-9O?AS5y`J>>FMkg2ntlMeRelK&RT}s7JUfZ@!Pi-cVw4<Ve z{`TE>ae$%~oO))8A$|GfAL#3^{}?&;db3pOiK3Vbm7-lsLCqW$rzmzSf~tuG+U5+V zVXEU)6!6+MGU2Q63p@-Ii4Dgv|CzlIPvV?FXa!eP9ecf5D)rhdDqD7VZzm^s6_5ed zvATFL*uvwpQIcZas^e4?@Y+@sZRN44Hk?X#VqekwO{wZa04bQlkd&h8*z3(wspp$j z5&tR*cDQ0W=(-9_a6EL`lmL_h1w*(07|LK40No;%f}xJHp|J7v=~MI2p+k`!3$Zta zudn2Hr!Bl(quZU34PWn{l{=8Jow`mDubt{-#iH18<D`TU$a~Ky*Vh6K$#q5U{o84> z<JsVyHjavRVh+^nSI&Eudi3OP<G3vyrl@GZ>8mX&%Art`E>G4<iL~>is6t7)JdqnF zTQ{Ikk}k)E6-v_O$=*DXcE(HIdgj!iH}mZ9Z6_!B?Z}atAEOE->2fHjP?9cB#4^d& z^)Dz%_7`Wmoo_5^VDG1sH4sUD!e1e9l7Sz8obiXr@Qttc&B~q5qToJHnKFgOefF7b zTeB1JoR=+IM&-N`?Y735a_lLxXE`S3g#FBjOTps0m=oerh)ZQ9_7odyQpbo#sU70F zm=oerUUq}0W9o{4lDL?YWiHUgoDh%7mP8bdjX7E8EM3eA@u+M`1U>0uPFD038*@TD zDq9lJ#heh2Qlg7FAs&@23Fu-@h({^W#hmQEuahaoedFu>r|5?ob5cVopo=+Kw*={8 zPKZY#E|m?@q9+8;7A#mm13vml>+7N9`t|GSHykF<w6FJ1?8%My1co@Gc}{{l8Mmk# zYa9&mT9?n)piag^I&N%nSkg<KOg~Vr^!5)$Q3D^DcCtnR$Lg`g&8<$RyWYT8h@6zj zW44X#!*dYS$+)86ojPiPxTcrbr<Xb@3}pZL`V@X_R+{E4nr<`u#-gsQad4bMF<+~W z`}!r%4A@reB>3+Fsfd5jlj}TcX9f=zv}*>dx>;V=&NpN9e@jtbDyKdCUjy1xJXsWE znAx*ux2K+D`2Jx+ck@Rk#fmp?-lW5a54WpDwOtRsGCpq%!8$1@@9io6?0Rb#Tc7lk zCr=XojYoS*RM+E^-Kx7^y?RCS=g$}25H%UW|2(2S#h+c@o)&Yv_kRJO_n_Zf_&8bs O0000<MNUMnLSTZc!f96k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/separator-h.png b/Templates/BaseGame/game/tools/gui/images/separator-h.png new file mode 100644 index 0000000000000000000000000000000000000000..339c0fbe039e8b5cbdd085230594099c26928157 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^Oh7Ed0V0oZ{G0)#BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%iF>*@hE&{2%E-w0@&CWQFtY@YFPFjrhT;U4EtAg|X#kZl Nc)I$ztaD0e0sy9+AwB>A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/separator-v.png b/Templates/BaseGame/game/tools/gui/images/separator-v.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0f873612cef214add9a2f88ed72dbafe92174c GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^GC<74!2~3aY}VEQQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JiX=Q;978H@CFNvfBrGsX`1Ak2{Z(F84g-d-`&k-}TiGQ5 P)i8Lv`njxgN@xNAx)CBs literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/slider-w-box.png b/Templates/BaseGame/game/tools/gui/images/slider-w-box.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ef04961a30ad99f1e8871ef1f292b8a58d99f0 GIT binary patch literal 982 zcmV;{11bE8P)<h;3K|Lk000e1NJLTq000>P001Qj1^@s6w^2g10000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#cu7P-RCwCFR!vM3K@@(oZI`u_7W)HS z_z46E5)-2a4+bql1U-1bs3#7@MD)a635mu~6G-Gn0+OI7O^h)fNi@NO!AOt<2?u@- z9*T%*X=%IvU1wGbyW4iRJ=m8_JMZl``|W!(^UWYsCMfWq4Q+02T9P!k*TKfx&%D0b zIAV74Q(YCX9ALy)joJOZchpx&6vet?Mob}N^dtoU6${^z1Pb{oBc|v~N>Z->n@DC( z^dn?hhVAVwNE{p-MuM=syre-K`>u)#Xl!f(o6S~Sxwf`8L6&6j`TR#QZC~prlSvid z+}u(e$w530ZEfwknBO1B>68EU^)=Huvgcc$9*<iWGW8YnHBDtQ(Kffo13Td`IA~gJ z#4>%OPjCcpe23-4*Ecl4pS?Z!vATK~Iiu<8eTHFFyaGybOBf*v0#((zSZ!Ussc*KV zOSc(IBP*p4NV1{K&1b;)3K*lE;}RUd_RdRpkQHcQ{vA+^sH8cEioJZbKQB(*d>ETm zuGaa}MSa=Fv8LiO$#h;y@AHDD?T@o!>D`6mNv2*6_aqXDx41j*a=9k%Ha_7s%C58F zFXwu=WDCZkai-igF-SdCvFG!V4BWm>!3*e*>_?`8!JubxadCuYS^C-62cdY{%V|mO zPrfEv*U!cyQ>QNWdN!6mk8mtUKTi&Y8cr}=J`yS#jShsv;o0u)ZhuWp4fOW*GG4ED z6l>Dt*u#5hb>-U;I2DTx9PIAQwsqd{y8?C4-gT3yI9WaFU@BB>UKK8*>A7oteeO!E zq@o~z9iMO)Iy!(tXb3;o>AT`6pKHBx%T2qhfX53+u`y74whPk8o`uzwE162EM<|a8 zw7SIcYMIM%i)G?$iUZ2&2bNcK>ca7I7FI{%a5zA~1PCEZ@l)(h;K<%ZdJU70*H7+D zBuN4uf3=Z>TqcahOA4g;Y!Y6wSi+(x<|H9;qX{^~k}4v-QVOC4iG*J!<RFm_Nj|)p z){rRw=EIPQASxD_kaFdi6B5tLM1tHTR^^iM%c7A8;9Y)Pdz_h^gG9u05|2fJc2*u= z1O--&o0Fu|gWp$HX0#GRz7I=G$Ox8XW$@SPCyYfpteA|3OwSWi8eA$Txk?$Plvo@; zPh{kqNPVH$To;KHQH??rm0%x*W>WFkKE!Y<#Qq8}0K%2(y+C}?TL1t607*qoM6N<$ Eg3Ob{L;wH) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/slider.png b/Templates/BaseGame/game/tools/gui/images/slider.png new file mode 100644 index 0000000000000000000000000000000000000000..92fee1e9c13401f0b9fb319b14ebd267b7c1c8a0 GIT binary patch literal 908 zcmV;719SX|P)<h;3K|Lk000e1NJLTq000*N000~a1^@s6itQw00000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#E=fc|RCwC7R!e9TK^UG%cGD%zt0ir6 zsaNrFDt#Q(y0z7`P+D41JXo}cDhPr%L7`ADk+z45heAW4^&q~Z(vt^?MKFD!fqD?c zi_p}1XdX?IeP;b<V;;NNKpWa0W@q-F@Bj9nZ)g5pjB5l19g^WkvoAG{JGTdv>BsNz zeu<3YC~{d@T`O@7=<fGw#kgCeSJ7JJM~<9WTWgEjKYdah@5hYds0vFw7|mCf!!GE9 z&{*PV?`THLOJDa*q>Bqd8zh+r&1N$TtpK!GELCNikCSDYg%%{_tjFWkfZXnes>n;@ zqR|aj)!gZHXh8b=y;^A;kRu8#6pO{!Mvtd3L02hDKJM?<79kE%E?3>4B+0?(MwHD# znh=bupw`5N2ruq9KZs6u_7))i@#~}<;40XaEJtI*eV$Cj55Bf2ineZ<81cX|$A?-K ziRQ$vUFqvirBbh8IBB!lrWT$p@_Epw4{y{Jk<89s7(yreE_ZXgJAlQLOs#F|`{D~e zk1JU{!ht}*1pQDv9-m=Z)*TLqIgIfHJjnm+Zki*YSv0N&+<5q!9OI!(a(m|ZDWCi2 z+7hQAJOK~#FZBQBsFq0$xe508a5Ng7>+0%qd%a%d^ZA(i`uZ`3VWtE40bY>a7M7V# zB!+jkHs_9=>37>2j-V5r7a4n<XN+d-({$@p*?Th8SS<6sJv|PW(}_euKor!N_O>=; z!uTf4JA}`P;v1uD=Z~Jd<ghpz5f8~JW(GAiccLt&RIHwy+D)gC&2C3L5En>b2XVYA zW9_{!_Y>&<@ydWoCaW7|c%=l304u9lXbBBDP18sKV$fMbp11YLDqKf%e#(kCvbjnH z0IY2MSwNH|Nr;Cm_2f`8%fP&(pfq1j&P&EykrGAGkevASCV(r$>yDU`^BZRlB!U6C zWJs>$^wwoAh)RXbi7W=16Mq@GgpA26$sz+uY<nBQsVr&>E0GPzMbJE%h#`yBRq)9| zM)ez$XR{;UB9VaZPe36jlnmZKpDj2gSsD4W{t>WPgB5pU{)$x*B(ajOltDF2!h9-! iq)BbrE0*;C1Q-Ck2#=cQD4UW10000<MNUMnLSTX>1gDw+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/tab-border.png b/Templates/BaseGame/game/tools/gui/images/tab-border.png new file mode 100644 index 0000000000000000000000000000000000000000..6703924d48fb96552fe11a91696f7c282aea3439 GIT binary patch literal 1203 zcmV;k1WfyhP)<h;3K|Lk000e1NJLTq001fg001fo1^@s6#ly*40000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$RY^oaRCwC#Sv_bJK@^@lu1FNL5y2vf z2ud(1Qdy{IVekjRq_Py0JJX0pu!@kF6sdxu*u*=u6{|!sYKVp64_XN(5){NFU{qqQ z-MZf--{sxg*}dJ(;t%-9GB<Pk=6&<#&3oTXa-K2%-`_8VQn5Jbw*yI(%9V=o^^)2e z^z{Z(=vjNYVzKCz%jFyfmKLVX$LSY=7q_+VHk+E-R|j%A&v;&LpnFGkXlVRHK(D`R z`!d3@_oh@Tc`yh)exJl%fuCPLxMABZEv=?$=f3JY3-6xV$?)7C9kE8G0N}BMd(8Yy zC7^fae4puVZE$+2p1pe`z2?*0eAQl_E1P+E#{yZLo2{8}YIMksV?qeM$QB?k_MR}6 zzTlQ(yzTIDJHHh6cO5w$um{kvH8%UR#*PJw6?FFWo2@O)78F-|>s+T@)zO{{b^^V< z=wH7wY(L{2P71F802ipY7kw@qE$vBN%q#5`<VsFnowDFy03IettUOGhhX)4mt^~<# z(_W*VY$ZsSy}TX(SV$a@F2fpRQr$3n0if_G1zwfg5+(2kMTS8VT^$BBc~riPgCjj@ zjrm|XNth9CEj`IFfPQhf5~|0Lfj&cs8A032GjC~-%zqezXOU`{7iFHrxCALrF6IIV z%&+q##$7Q<U2V?-MRGlL?Xh|DV{>37@CZ`H-O1-w@Umr|4D%f4*L^Nt+hhLK_6p2Z z%jmzE=C=LL3e%yyDGwhjj6U6g3bP`N-;PET+g?oxjXD4|r+B4=CaKr15}L$b?q6b0 zh8~SA%A;{SQ}t+Cpt=UfVlxhaEO#3>tT*c#8_eR@?>5%(PLkvwCV9zbSc8!+a4ONV zR-+KOJO_Zt7V|Std?$P6?aK-C`OA{+V;uY;3(0FXgFHquf);&@Yc?a<)A*xjHOZUF z9OEwblp3gHGX%+c&yuRyjAl==LJ=%0nk$@AZ@u)S6zSP8LLos?q-QfZ^*VWDa!G<w z1~ut$fkg?%Hd9biA&5~qzGjbV$|Z%d8zrQNIyJe**90`mV^QCy)k_7GY$i(Tv8OO` zv8U8P$!59^?X@1jmLWFra6F1VAqPVbA%fV%gV-bqb`^_Mhbax431d$@d{GrGHBfqI zmG0GHGinTAvH}!JFaEQqjCt%IWlu@$WzL=^Z(okZp0qvLy6k1jo|ul%MzJStPqQv* zFH`me1N!1X-quCkJ%zEZ$h#*^Oe7(RVo&ns4;~~TLHJ<4dUsFU4gLE*ls&m#N^<uE zQ1oOLKR@c;Ju&{^8T8{23jicY>;yi`d#otZB4azuR)9UhD?F)aac|0iuXn*Eh1*_F zk5@SQ_F3yW*%QGMWE}P+4H9G+d(!r(D?=_|4>u*qe>cw_Sh<T|&WEzaIDugdWfv{_ z(0es8T74gV>R}v@)e(9wdwjG#i_h1~oIT;xGv>0sd%E<S{?D|g|4;oAU;sZF1oiaL Ru7dyo002ovPDHLkV1gC9ElvOc literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/tab.png b/Templates/BaseGame/game/tools/gui/images/tab.png new file mode 100644 index 0000000000000000000000000000000000000000..ecd81daf74ba100b0c51c5eb68fd4e4480cbb7e8 GIT binary patch literal 939 zcmeAS@N?(olHy`uVBq!ia0vp^&Oltj!3HE(mix;BDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49vcsE{-7;x8BS;-6iEH(&k@&B(hUU!{sFFrdeA}-npDm z+T0<q>HL~qg_GQ82uO0A(c*t_B$Bg|A+hBqOAX`oNY)}p=B6D5%h%3b^=Z<zmy^n( zJi<T!KjXOKu*mb8*y-<rms+~K;eT|==kl2i_qo&bXC9sLbjRYtTHk4zT9Z$N?Ws*O z+blg#`ZI4$>7j%x(QBs}%ka&;nDJ!IQMaC;qNArjKlu4!Pet9K=Z8y<Ee_I}sxj9u zSZiw6k%H%yA8IP%Z0z^{Kc4tv)-u;(-iRkl-!mucu|=|MJ(InUpXUUtviQ`khqB*B zyzP^a{4OCWkifu(g8t-ZYH;jM*kftE+`WJPrT6jnW-@1gEiJApTj#@hM*H6U`};3W zPCtIAJ#*9Yr1kL<k5(M8IF}<M8Zfux=ardGvp?$fbMX|09H`xwVDNj!`RA)|=7gCX zdHiwf<_I0FuPNal{nUg%I<Pk%+#R?6d&2y?OAa{}aO4SSWluf*iSh9Yhc|o{T0hif zL|JN>&$_0r{Sal%;m-QK=Dfh<EW6Jor*0iywcsO{#J0D0JJ;B*;gWjKbgbmn-JMHp zw-_)T(@yYFo$DjoY-r2Eb9-73PxQu#iSjIMx-aj9@wCax9LssO_rnRW36E@<4OA{K zxtmvQC3WUi`24A!DdBt8|EYVrNa0P>LGD9wQDT9iS|uLbH|n?#9ZCG~{-5=>9j4w~ zGdIsY-0mMQ_Vec+MH%Z&?2@N9>1F(WXJLHWWa><(NAGr|=8Cqwe3ntDfA7A@!}8Uq zpB8Nm(3r69^%M`4mdA;D?)ATFQ=fHM-}_i`W?#bdD{sqIUVa&47!bNYI{T;rkC)oy zY=KqX&kufnSSiSL=yYO#(}wJ7M@NeruPgMlvRkj;a@62>?96MxzzjpmTZEpgZH;={ zw^%Wge>I<=KGz{><->(0o`we|{yMZG_G6L!H6EP>4U7_4kYMgnzx(^Xmzc~vS(A8W zUB&FW#5r?pzJI%-@A-;*vC7AL_v*ghW#u{)VT`a4ZZj%F;Q#{<D(d<0Vm$*hL+L_0 V4bcGKqrlw9;OXk;vd$@?2>@x~oUi}@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/textEdit.png b/Templates/BaseGame/game/tools/gui/images/textEdit.png new file mode 100644 index 0000000000000000000000000000000000000000..cc3da5f85f2dae5c0a4a705d3c0d6ad66a655e46 GIT binary patch literal 2989 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/textEditFrame.png b/Templates/BaseGame/game/tools/gui/images/textEditFrame.png new file mode 100644 index 0000000000000000000000000000000000000000..5a65fac3cedf36c09e5add1c874775d8e364b343 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^{2<K11|+SoP4xm&k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X=6Jd|hE&{2syTh&+x$kK1DlF&wL~cFl~5Cy7kYp}#5u4< zPfb%(GFx0?-;BgtTQdLi%UVtO@c(~(vH;somSe}%${rqSZTb8A`+xRM!$zM?Tet4@ zSrh#A>(_@_GbJ<_dZw^Q+&I7EPrEnI(eqE51YbYz7T13zwEE$<x3>$n>MSVv_3!WR xqwIV#4BRgm4JXvHMKX&S7St`!IKaTf!=UB9TBWi;$P?&J22WQ%mvv4FO#lthVzU4M literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/textEditSliderBox.png b/Templates/BaseGame/game/tools/gui/images/textEditSliderBox.png new file mode 100644 index 0000000000000000000000000000000000000000..57a0c49d31811ff357bbc97b755a1324f86ecca4 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^{2<K11|+SoP4xm&k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XT0C7GLn>}1{W*Vt!Q~0#wD#}s4RdbqW7eB6bLvrp+FK?f zt}l|}=E?DV>hj{@xxCExH~$gd|NsA|GbFPKu%;#pGOuKP$+%nM%z=dh(<OY2g_&6! zCQf2VW@10`|9?*b57UVnoumy17N5Auyu4C_{l=G<m!&(dHmsTU@!;R;|0mB%Ffg() YC<`vW>HqXjD9}9&p00i_>zopr0D_oRm;e9( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/thumbHightlightButton.png b/Templates/BaseGame/game/tools/gui/images/thumbHightlightButton.png new file mode 100644 index 0000000000000000000000000000000000000000..9d83b75f31b965b79ff082e3a0f6e37e4c757c1b GIT binary patch literal 778 zcmV+l1NHogP)<h;3K|Lk000e1NJLTq000mG004*x1^@s6?#63R0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!tVu*cRCwBA{LjDuMC1d;*o-O^fkiXg z{;yiMgArX}Sda%?{P@vB40m4qW!Sv5^FIqnKvqtHY!|S=7>^%4Aj<$I1_}WqRRR$d z1~4y>8i*8ofd!i&&^88q;$hSbL@LC>f3!$o1Vs!3BNnyT#Q(FzWmcoc0=>O348mYE z7Di)XG#${gULYk9sZ%c;*}t7K1LT!85hA<S4?rL;W;O~3vLk?4@c+~hd&cZF2Qfkr zA^skSR{;4dLBfm-*=xcXxCC4nct!fZ)aqa3CXkdFn=WWEe122N@a@AMpdl-mz%~Ol z@A6Z*cI@I;f=>QlXK)Rq9_nInN)!ayOp3q3h5+?ZB@mfF0rU0$f5zPxzJM_-aIs(% zaacePd~O^y0Gb=Qf!I}_n|uR62^f})T}Q)U#D)ReA_iudz%LMg;eTdyg}>q%QN>TK zMd@&GqN(S^VIe239t#(mI2R5BxNsT3gC@>{!vG#!2JoSY^WiXn50?SFXyUv$4B(}@ z3n;D^SU}}Ft66XpQhe_P#<w$;bbuoM`_$SxP=yQf!u#L9e=D)FvLdTj!eJp;9H^cF zT1$h&U<Jd?L*c*G?d~HPqKU%*O|a&hhZMp37#IRrV*j37x%ivWZ-(=mLV;5JQ0HMa z04n|wh?fAJzYb)`AZwvqjTgJ7B`-x@3uUnh>$P)N-;>uu0ogpTEtG2ypD`}kb`^|A zTPQ=eg`y=V3C3g_aP84^23RtV84ZIG8wR5-l+hN-XbXi3Efk;Vaufm3qytH#c_6Z< zE(Ia9w3GDg3<4Lg-UZX@LvA#W$v3*3@#f9jjG!4Dq}d&UEfkp8X#0Q~<I27<72uQz zOGsg(t+dft7!8BbFc`V@0=ROWF>5|tAdDOXK#LK`1pomC0NWAfc{C??-~a#s07*qo IM6N<$f>^aj6#xJL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/toolbar-window.png b/Templates/BaseGame/game/tools/gui/images/toolbar-window.png new file mode 100644 index 0000000000000000000000000000000000000000..fa57993c2d6caf27896fb31027fb064448b4afd2 GIT binary patch literal 1938 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 UZONBgx1KEE<&JY}bPdk@8~<5_vH$=8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/transp_grid.png b/Templates/BaseGame/game/tools/gui/images/transp_grid.png new file mode 100644 index 0000000000000000000000000000000000000000..e6b9db4ccbc67cf5612c769c42772f53d6986072 GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgfv9&u3_A=4$ly@5iKC9V-A&iT2y zsd*&~&PAz-C8;S2<(VZJ3hti10pX2&;y^{(o-U3d7N?W{{Qv*Ip3UaLnVH5vmPpQ! t&|q22#>2)O!EtnRXXnWb1-^P_hAYOLO<FTK#emuvJYD@<);T3K0RUaTI;H>s literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/treeView.png b/Templates/BaseGame/game/tools/gui/images/treeView.png new file mode 100644 index 0000000000000000000000000000000000000000..cbc20d49e062d2cc1ebf2cc6b68888f9b0e5dfea GIT binary patch literal 456 zcmV;(0XP1MP)<h;3K|Lk000e1NJLTq000pH00A5b0ssI2uI9+c0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzYDq*vRCwC#nz4<8Fcd)JQzR-715nUW zAu$3YumY3828_T6w4q}IC}^mBmXM+dY~Ptk2}<4<MKF07%=hEViI5lP5K>AQ7(LVX z{k}0tlJ&#Ybh7(5_VMGMqA(|CggnxZ%D3It-)`FWbz`hQF?Za(gRhS=%u|@x%~R?F zn6uX*yca&18HS-O%XyxcWm#K1&x@j{X__r0AEK(N$3tNlc3mfTaoOzOF%zI+R?GtE z85QF=N^_70=dP<5H@tIp@9<3V4$ONUvN^0KteZo8-*<C}2{1Y&2m)#syu-NRofCP7 zXNq?ix8sav{Y{DC4`@FP^E_8m)$*>_Fj<zVb6GGAlcwo<2`0?OD7k4F;~Jy_+P3}g z7^wSkUf(H-!UEtoyn}b}&XeBJ{5}`Qu>#0Zyn}ZRdPiDA)6X<bLWsJq9r~f(A+PZc y-l5*XJ74V`{+|c~1M>_+y#oUS^J?Z-fB^u>9k?-a9w5E|0000<MNUMnLSTZbP03^c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/treeview/default.png b/Templates/BaseGame/game/tools/gui/images/treeview/default.png new file mode 100644 index 0000000000000000000000000000000000000000..ceadfa8619b4178d633653dfccf2432bbb33264a GIT binary patch literal 375 zcmV--0f_#IP)<h;3K|Lk000e1NJLTq000mG000mO0ssI2kdbIM0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz8A(JzRCwByQZ3U&U=(FXlXY1p>#~o= z_yL*`4bhB0p)Q$-YE&vJm28jKJH}WW++f^uzt4HdbzO-kM*Rx3Z5u^V-}m@u7)BID zMNziEdS`i_o2FS5#XQe&tEzGw$Md{xo$u^%9Fg61-5EqBYToZRrD@vjN-WEsU7Dt4 zS;mrts;b{Z0A3~>`0LlvVHjSJWm)jODcA$BBuUPAS(btz+!SP4#&Mi&+i99k)3k%& z7xj`PU3EYP@?6)&(cm6eqHEJMbzSSajz-jFG-5o@?-YR+%d*g7R}J!QHvm0IKx`O> ze?|YOGz<eLVUs=ksx-8W<M=`)K<IV2<nj3MczoZ#r5K}vAlQeHPhabccxWF11_1MQ V5mA||2pIqX002ovPDHLkV1jH@q5J>< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/treeview/hidden.png b/Templates/BaseGame/game/tools/gui/images/treeview/hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..0fbcf3840ec5db3b6a6856ad3d312477c1569cf1 GIT binary patch literal 420 zcmV;V0bBlwP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzMoC0LRCwC#k<Utma1h7G{~`#Iptu(g zOR#v*TSPC=3v}Bj2s(ENy2V0WJM;uSL~js52R#@RdTVcnu^_0Z&4`BP-n=&O^Y?S+ zJ0Io`(=-jtF&1XpKLCKI55q7JhT&nJ=bNgkPSK^JZ;KmR;Csh$+><0pSB~R+kbbzi zwt^rCF0w3p$K<m<&?N)QvY&NbpJ4ozbbDJUief{zs;cd!X&Nk-%T|END}oV-bO>2b z08}C+S(df8PXf!byh7a@LLMdn?DrdkpeRb~Ow$yiC>jB%BGes;qIf<5pz}6Kk{!~) zG|lmpc^-6K2b@p;|1yj-G*Q|D4G8<aw{5!>6O7C-48Q?G9LF@!Yu9yehFkarblX^$ zWqF4cE$~1$U^-ht5H2zK@$VW(fZlxHKStj{C;sw0?|B%X0Or__00RIt*_qNQN(>YL O0000<MNUMnLSTaKUaf2Z literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/upDown.png b/Templates/BaseGame/game/tools/gui/images/upDown.png new file mode 100644 index 0000000000000000000000000000000000000000..3772178977afcdb74526d6dbce036e7b0df7109d GIT binary patch literal 3146 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_d.png b/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..ad481414d41fe953fe6e70b9247f2548b9c6e35b GIT binary patch literal 280 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xc6quuhE&{2`t$$4J+o>9tIM71sV-)%g7evqp8g+rW%B`v zq=R31)?YJg%Gl%g>(+&aMe_uou%-wvogS<7;?45Fssc0NGdC54kBZMv|MB^|z9UZ% zdl3Jn<4rlA=G)hI@Y$q&F<@xCKShe6NX{t1*V9qLE}idP#*wG2VM=@2uXE3Fso-BP ze$RJK$AbkY4FjY)G+r`Hu}S==B6j#o;*ul$zOOlMX$6QMX)xAb5xd9ZLPPkEPfRO! c3Mw36kO-aCX!_JB0_Y0{Pgg&ebxsLQ0FlCP0{{R3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_h.png b/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..0de6d9ebb3602936b2a6e74ed07e6eeb13169b50 GIT binary patch literal 295 zcmV+?0oeYDP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$w@>(RCwBAyr;wPp8)|F8Sntb%l{ef z>oWdF;xn><7-GTo46oKUg6RMF4EWm6z<{hCY$LJ@q?cV{AOxO9=^?v_kqKl7$VBnQ z=NbO|{Ywyo)PdB)yvqVJ?a!Y-48#IvBsScD-;^2vavm~ZO-X^Xf6tkN(=eP~U`1l% z^Bztwa3ZmP6Jr3n7v4>rh~S{fF;E(a$bboyJYoJM7Q>P?MoRq9w2q*S0(C(QIP-$q t#ft<Dc!e)7kdqr3|NsBbKnVZ{FaQ=knM-^4DfIvV002ovPDHLkV1f+PdD8#@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_n.png b/Templates/BaseGame/game/tools/gui/images/uv-editor-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..5ae9ded23e242d5e06cfa8e1faaffce61ceb317c GIT binary patch literal 299 zcmV+`0o4A9P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy%}GQ-RCwB?Q(FpzFbqtV{qQ7$XYi{~ z3SPw{=(lGQO2K=05}`_VBh(H1XzMyqn$pQM4H@jZj^G7^2!>&RzVAt#@y>zP8k}>y zQ%qo*CXjr_E)Wh?RY3~O4g4U0{Bl{Aux(pXplw^2=b1(DsDP1^{0)bI_x_r|dj;0o zBR0mI7m$eEWql!b_p^e!uFqAluIpb-L?Yq|qoOFXla^%xrPMwLD80}$O)_~LN9hf` xh1m|xN-fF%@In~Rm>ZuQl3v(*a0L$m1^^?}fiB+h)=B^X002ovPDHLkV1i#Kd<Xyl literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/visible_d.png b/Templates/BaseGame/game/tools/gui/images/visible_d.png new file mode 100644 index 0000000000000000000000000000000000000000..329dc6a58ce1b97800e475ea59f6f2ffe90483d9 GIT binary patch literal 390 zcmV;10eSw3P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzC`m*?RCwBA{Qv(y10{fofkH403}65S zA`F8-MMZ^;ySw{sK|#TPOiWDw<>cgkgJ=*RBo2~C){A05a&qz<kYbny7#oDOw6wl~ z3<HUS)L}IMrpeLK5v(aNF!29|4IBQW8w669nVH!R(t{EPW@cu0{r&wJ>g(&Vde`3G zo`Id6ol`|c<>QJKE6Op#0I2y~Vq)U|_wV1M*b2jqjg9~B-@pG~NJ!{ERu^b$YUY8Q z{P5vJhWz||6x#&^1Q_Dt;ut=D{0Qdr^Yd@T8VjHR0@(mEXu*O7U|T^L<f;P)4uEZs zjg4K8H4tF{<UCM_D=8^~0~CZoY>*g84mlbbku@?hGNL%UxVSj{_wV15AT}#2>+{~; k-j(QSh;hJkBS3%w0GUPCDCt`^5dZ)H07*qoM6N<$g7vDUg#Z8m literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/visible_h.png b/Templates/BaseGame/game/tools/gui/images/visible_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5125d50efa11c328ef5a68e21c474925e6ca2e88 GIT binary patch literal 389 zcmV;00eb$4P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzCrLy>RCwBA{Qv(y10{fofkH6w1~7oa z02U4v6%{t_?(Vk*1qJ^xF){s@lau=mqCtF+I7l8tAB>TloID4l7^VTn24O8Nt#2U1 zK;j^ESPg(_a&&YAYYGeu{J&wthX3dWfz)MYX12rh!Nb7J%<Qhezdu8LeLYsM+S}VR zu(Pvss;H=ZT(M$BIYt-&HJ?jNO#J`;{d*K!VYsoe@&En%_x}qC3H`_F0!>ZLJdl$g zK77cKpP!FnyMTZILtI=O!^e*w!F+yx{;gPJ0Te(W8$bpvSg-(WD+q&Jb>P4Ou<fz2 zvFou0A`F0>2MTc|B_(ixf-r~;5(CMhTg(X4%*e=y;_Tw$;_%<Ue@lYctgNihdwY9V jqNg9QzM-F)00ImEFele2x-v5*00000NkvXXu0mjf;iaS( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/visible_i.png b/Templates/BaseGame/game/tools/gui/images/visible_i.png new file mode 100644 index 0000000000000000000000000000000000000000..eb5e3b267154b663ab439ab14ce90c201f2d8d1f GIT binary patch literal 418 zcmV;T0bTxyP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzL`g(JRCwBA{Qv(y10{fofkH6w2C%>w zjEsy7*REYt{rU4}+uy%`-GM^9K(S}QFp>hYKQl8k?__6ZuhP=ex(!kf3p=<AE?v5m z^X=QWTR_brFfpiR1|}w^r~m%_vjWPW1*ya8g<rpZwSj0}US2RIARxe?tgH-{18UX) z83r;4qz-6EG=>2nCxSFdN=jn&E689D4h{{Vy6ZqgrehcY^zTQYBm);07nuLy!v}`D zckiMY0CEvfof#V&TPB79K*7$pZ{L0fn$93DE{+<~EG!Jd!om!|@PYA9!7YY`CMXs_ z0R;5#WO;dcuwsy5ARs0t#sCaJhI{w!fdaHvPfxD}t`FG&Xlz9P{{6cP=prc~h6OMP zKVxNOEdy#^iX4p?2Ef3@ix(|`VnR^G@AUQcPaw-<7%+760ziNP0G17z(yd<?T>t<8 M07*qoM6N<$f}Fgj1ONa4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/visible_n.png b/Templates/BaseGame/game/tools/gui/images/visible_n.png new file mode 100644 index 0000000000000000000000000000000000000000..77d302629e51d35f8541f68b2ce33fd5d5616e30 GIT binary patch literal 464 zcmV;>0WbcEP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUza!Eu%RCwBA{Qv(y10{fofkH6w2C%>w zjEsy7lO|0ve(>PIw8xJh8~*t5gO!hu@5`r8pLjSpIDX5@%AQwHP{_*5%R38F4+}d+ z7^Ahdwd>@`lcgXwNCU_KnBk(LqOX7c{K@(2*DrQ`ef<eFH8o{0eQ+;aym+wyMC<D6 zGJrH&SXeMrS674OUcY`V3UUcAFYgDCy7u<=GK??)IT55OGBOgYUqJ@*@bHKV3kyF# zckWyZMi{WMv3=s^=4Mb-R0Q)+oHzl-$Obq$IWZIz6v!DF8I@zWKuSug;^@(%zhAw2 z#o*`XhhjU>X0Yb3U%!I+K#TUm10Nomz*uN$Y3aIp^=fH;em=vOFJC}@2g~{T`ZC<V zf1jbPt&KrVO>J&tV`B<NEPxCE0Z;&4xpJkJlarH2OiT<c26EN+@87@Z=;$<8R#tXl zjYgOOFwoc6=kxdPU$6<x%*?Nfii)-%%VQWYbn^m0fB^uRgQY;(AsUAO0000<MNUMn GLSTY|e$4s+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/images/window.png b/Templates/BaseGame/game/tools/gui/images/window.png new file mode 100644 index 0000000000000000000000000000000000000000..d9e8006e473974be6c97716e9ab9abc2e0e373e3 GIT binary patch literal 2559 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 P=l~NVbHhq~mze(oSjCIE literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/gui/materialSelector.ed.gui b/Templates/BaseGame/game/tools/gui/materialSelector.ed.gui new file mode 100644 index 000000000..a4d2f6078 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/materialSelector.ed.gui @@ -0,0 +1,2003 @@ +new GuiControl(MaterialSelectorOverlay, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "GuiOverlayProfile"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(MaterialSelector){ + profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + resizeWidth = "1"; + resizeHeight = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + position = "72 98"; + extent =" 766 550"; + MinExtent = "383 274"; + text = "Material Selector"; + closeCommand = "MaterialSelector::hideDialog();"; + EdgeSnap = "0"; + canCollapse = "0"; + visible = "0"; + + new GuiContainer(){ + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "4 22"; + Extent = "120 31"; + Profile = "inspectorStyleRolloutDarkProfile"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 23"; + extent = "30 16"; + text = "Filters"; + }; + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "4 39"; + Extent = "120 507"; + HorizSizing = "right"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "128 355"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiDefaultProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + + new GuiDynamicCtrlArrayControl() { + canSaveDynamicFields = "0"; + internalName = "filterArray"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "18 0"; + Extent = "128 195"; + MinExtent = "8 8"; + dynamicSize = "1"; + rowSpacing = "2"; + colSize = "128"; + rowSize = "18"; + }; + }; + }; + new GuiContainer(){ + Profile = "inspectorStyleRolloutDarkProfile"; + Position = "128 22"; + Extent = "480 31"; + HorizSizing = "width"; + VertSizing = "bottom"; + isContainer = "1"; + }; + new GuiTextCtrl(){ + profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "133 23"; + extent = "53 16"; + text = "Materials"; + }; + // Create New Material + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "594 24"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialSelector.createNewMaterial();"; + hovertime = "1000"; + tooltip = "Create New Unmapped Material"; + bitmap = "tools/gui/images/new"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "578 24"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialSelector.showDeleteDialog();"; + hovertime = "1000"; + tooltip = "Delete Material"; + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiContainer(){ // Materials + profile = "ToolsGuiDefaultProfile"; + Position = "128 39"; + Extent = "480 507"; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "480 507"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiDefaultProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiStackControl(){ + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "128 0"; + changeChildPosition = 0; + changeChildSizeToFit = 1; + + new GuiControl(){ + Extent = "0 4"; + }; + new GuiDynamicCtrlArrayControl() { + canSaveDynamicFields = "0"; + internalName = "materialSelection"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "3 0"; + Extent = "128 0"; + MinExtent = "8 8"; + dynamicSize = "1"; + autoCellSize = "1"; + rowSpacing = "2"; + colSpacing = "2"; + margin = "2"; + }; + }; + }; + + new GuiContainer(){ + internalName = "materialPreviewControlContainer"; + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "480 20"; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + Docking = "Bottom"; + + new GuiTextCtrl(){ + profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + extent = "100 16"; + text = "Thumbnails per Page:"; + }; + new GuiPopupMenuCtrlEx(){ + internalName = "materialPreviewCountPopup"; + Profile = "ToolsGuiPopUpMenuProfile"; + Position = "104 2"; + Extent = "40 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + Command = "MaterialSelector.thumbnailCountUpdate();"; + reverseTextList = "0"; + Text = "16"; + }; + + new GuiStackControl(){ + internalName = "materialPreviewButtonStack"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "480 2"; + Extent = "0 16"; + dynamic = 1; + dynamicPos = 1; + stackingType = "Horizontal"; + changeChildPosition = 1; + changeChildSizeToFit = 1; + padding = 2; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "20 16"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "MaterialSelector::firstPage();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "First"; + hovertime = "1000"; + text = "|<"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "20 16"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "MaterialSelector::previousPage();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Previous"; + hovertime = "1000"; + text = "<"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + new GuiStackControl(){ + internalName = "materialPreviewPagesStack"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "0 16"; + dynamic = 1; + stackingType = "Horizontal"; + changeChildPosition = 1; + changeChildSizeToFit = 1; + padding = 2; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "20 16"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "MaterialSelector::nextPage();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Next"; + hovertime = "1000"; + text = ">"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "20 16"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "MaterialSelector::lastPage();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Last"; + hovertime = "1000"; + text = ">|"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + + }; + }; + }; + new GuiContainer(){ + Profile = "inspectorStyleRolloutDarkProfile"; + Position = "612 206"; + Extent = "150 31"; + HorizSizing = "left"; + VertSizing = "bottom"; + isContainer = "1"; + }; + new GuiTextCtrl(){ + profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "618 207"; + extent = "84 16"; + text = "Material Tags"; + }; + new GuiContainer(){ // Filter Selection + profile = "ToolsGuiDefaultProfile"; + Position = "612 223"; + Extent = "150 295"; + HorizSizing = "left"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "128 195"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiDefaultProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiDynamicCtrlArrayControl() { + canSaveDynamicFields = "0"; + internalName = "materialCategories"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "128 195"; + MinExtent = "8 8"; + dynamicSize = "1"; + rowSpacing = "2"; + colSize = "128"; + rowSize = "18"; + }; + }; + }; + new GuiContainer(){ + Profile = "inspectorStyleRolloutDarkProfile"; + Position = "612 22"; + Extent = "150 167"; + HorizSizing = "left"; + VertSizing = "bottom"; + isContainer = "1"; + + new GuiBitmapCtrl(){ + internalName = "previewSelection"; + HorizSizing = "left"; + VertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + position = "1 18"; + extent = "148 148"; + bitmap = ""; + }; + }; + new GuiTextCtrl(){ + profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "618 23"; + extent = "84 16"; + text = "Diffuse Preview"; + }; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + position = "612 39"; + extent = "150 150"; + bitmap = "tools/worldEditor/images/terrainpainter/terrain-painter-border-large"; + visible = false; + }; + new GuiTextCtrl(){ + internalName = "previewSelectionText"; + HorizSizing = "left"; + VertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + position = "613 189"; + extent = "149 16"; + text = ""; + }; + new GuiButtonCtrl(){ + internalName = "SelectButton"; + HorizSizing = "left"; + VertSizing = "top"; + profile = "ToolsGuiButtonProfile"; + position = "612 522"; + extent = "94 24"; + text = "Select"; + command = "MaterialSelector.selectMaterial( MaterialSelector.selectedMaterial );"; + }; + new GuiButtonCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + profile = "ToolsGuiButtonProfile"; + position = "710 522"; + extent = "52 24"; + text = "Cancel"; + command = "MaterialSelector.hideDialog();"; + }; + }; + + new GuiWindowCtrl(MaterialSelector_addFilterWindow) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "362 333"; + Extent = "272 99"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Create New Tag"; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "tagName"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 35"; + Extent = "196 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "12 35"; + Extent = "52 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + text = "Tag Name"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 68"; + Extent = "126 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + text = "Create"; + Command = "MaterialSelector.createFilter( MaterialSelector_addFilterWindow-->tagName.getText() );MaterialSelector_addFilterWindow.setVisible(0);"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "196 68"; + Extent = "64 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + text = "Cancel"; + Command = "MaterialSelector_addFilterWindow.setVisible(0);"; + }; + }; +}; + +$Pref::MaterialSelector::CurrentStaticFilter = "MaterialFilterAllArray"; +$Pref::MaterialSelector::CurrentFilter = ""; //ALL +$Pref::MaterialSelector::ThumbnailCountIndex = 0; + +new PersistenceManager(MaterialSelectorPerMan); + +new ArrayObject(UnlistedMaterials); +UnlistedMaterials.add( "unlistedMaterials", WarningMaterial ); +UnlistedMaterials.add( "unlistedMaterials", materialEd_previewMaterial ); +UnlistedMaterials.add( "unlistedMaterials", notDirtyMaterial ); +UnlistedMaterials.add( "unlistedMaterials", materialEd_cubemapEd_cubeMapPreview ); +UnlistedMaterials.add( "unlistedMaterials", matEdCubeMapPreviewMat ); +UnlistedMaterials.add( "unlistedMaterials", materialEd_justAlphaMaterial ); +UnlistedMaterials.add( "unlistedMaterials", materialEd_justAlphaShader ); + +function MaterialSelector::selectMaterial( %this, %material ) +{ + %name = ""; + + if( MaterialSelector.terrainMaterials ) + { + %name = %material; + %material = TerrainMaterialSet.findObjectByInternalName( %material ); + } + else + { + %name = %material.getName(); + } + + // The callback function should be ready to intake the returned material + //eval("materialEd_previewMaterial." @ %propertyField @ " = " @ %value @ ";"); + if( MaterialSelector.returnType $= "name" ) + eval( "" @ MaterialSelector.selectCallback @ "(" @ %name @ ");"); + else if( MaterialSelector.returnType $= "index" ) + { + %index = -1; + if( MaterialSelector.terrainMaterials ) + { + // Obtain the index into the terrain's material list + %mats = ETerrainEditor.getMaterials(); + for(%i = 0; %i < getRecordCount( %mats ); %i++) + { + %matInternalName = getRecord( %mats, %i ); + if( %matInternalName $= %name ) + { + %index = %i; + break; + } + } + } + else + { + // Obtain the index into the material set + for(%i = 0; %i < materialSet.getCount(); %i++) + { + %obj = materialSet.getObject(%i); + if( %obj.getName() $= %name ) + { + %index = %i; + break; + } + } + } + + eval( "" @ MaterialSelector.selectCallback @ "(" @ %index @ ");"); + } + else + eval( "" @ MaterialSelector.selectCallback @ "(" @ %material.getId() @ ");"); + MaterialSelector.hideDialog(); +} + +function MaterialSelector::showDialog( %this, %selectCallback, %returnType) +{ + if( MaterialSelector.isVisible() ) + return; + + %this.showDialogBase(%selectCallback, %returnType, false); +} + +function MaterialSelector::showTerrainDialog( %this, %selectCallback, %returnType) +{ + %this.showDialogBase(%selectCallback, %returnType, true); +} + +function MaterialSelector::showDialogBase( %this, %selectCallback, %returnType, %useTerrainMaterials) +{ + // Set the select callback + MaterialSelector.selectCallback = %selectCallback; + MaterialSelector.returnType = %returnType; + + MaterialSelector.currentStaticFilter = $Pref::MaterialSelector::CurrentStaticFilter; + MaterialSelector.currentFilter = $Pref::MaterialSelector::CurrentFilter; + + MaterialSelector.terrainMaterials = %useTerrainMaterials; + + MaterialSelector-->materialPreviewCountPopup.clear(); + MaterialSelector-->materialPreviewCountPopup.add( "10", 0 ); + MaterialSelector-->materialPreviewCountPopup.add( "15", 1 ); + MaterialSelector-->materialPreviewCountPopup.add( "25", 2 ); + MaterialSelector-->materialPreviewCountPopup.add( "50", 3 ); + MaterialSelector-->materialPreviewCountPopup.add( "75", 4 ); + MaterialSelector-->materialPreviewCountPopup.add( "100", 5 ); + MaterialSelector-->materialPreviewCountPopup.setSelected( $Pref::MaterialSelector::ThumbnailCountIndex ); + + Canvas.pushDialog(MaterialSelectorOverlay); + MaterialSelector.setVisible(1); + MaterialSelector.buildStaticFilters(); + + MaterialSelector.selectedMaterial = ""; + MaterialSelector.loadMaterialFilters(); +} + +function MaterialSelector::hideDialog( %this ) +{ + MaterialSelector.breakdown(); + MaterialSelector.setVisible(0); + Canvas.popDialog(MaterialSelectorOverlay); +} + +function MaterialSelector::breakdown( %this ) +{ + $Pref::MaterialSelector::CurrentStaticFilter = MaterialSelector.currentStaticFilter; + $Pref::MaterialSelector::CurrentFilter = MaterialSelector.currentFilter; + + MaterialSelector-->filterArray.deleteAllObjects(); + MaterialSelector-->materialSelection.deleteAllObjects(); + MatEdPreviewArray.delete(); + + MaterialSelector-->materialCategories.deleteAllObjects(); + MaterialFilterAllArray.delete(); + MaterialFilterMappedArray.delete(); + MaterialFilterUnmappedArray.delete(); + +} + +function MaterialSelector::buildStaticFilters( %this ) +{ + // if you want to add any more containers to staticFilterObjects, here's + // where to do it + + %staticFilterContainer = new GuiControl (){ + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + parentGroup = %filterArray; + + new GuiContainer(){ + profile = "inspectorStyleRolloutDarkProfile"; + Position = "-1 0"; + Extent = "128 32"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + }; + new GuiTextCtrl(){ + Profile = "EditorTextProfile"; + position = "5 0"; + Extent = "118 18"; + text = "Types"; + }; + }; + new GuiContainer(){ // All + profile = "ToolsGuiDefaultProfile"; + Position = "415 191"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + parentGroup = %filterArray; + + new GuiCheckBoxCtrl(MaterialFilterAllArrayCheckbox){ + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 2"; + Extent = "118 18"; + text = "All"; + Command = "MaterialSelector.switchStaticFilters(\"MaterialFilterAllArray\");"; + }; + }; + new GuiContainer(){ // Mapped + profile = "ToolsGuiDefaultProfile"; + Position = "415 191"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + parentGroup = %filterArray; + + new GuiCheckBoxCtrl(MaterialFilterMappedArrayCheckbox){ + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 2"; + Extent = "118 18"; + text = "Mapped"; + Command = "MaterialSelector.switchStaticFilters(\"MaterialFilterMappedArray\");"; + }; + }; + new GuiContainer(){ // Unmapped + profile = "ToolsGuiDefaultProfile"; + Position = "415 191"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + parentGroup = %filterArray; + + new GuiCheckBoxCtrl(MaterialFilterUnmappedArrayCheckbox){ + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 2"; + Extent = "118 18"; + text = "Unmapped"; + Command = "MaterialSelector.switchStaticFilters(\"MaterialFilterUnmappedArray\");"; + }; + }; + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + parentGroup = %filterArray; + + new GuiContainer(){ + profile = "inspectorStyleRolloutDarkProfile"; + Position = "-1 0"; + Extent = "128 32"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + }; + + new GuiTextCtrl(){ + Profile = "EditorTextProfile"; + position = "5 0"; + Extent = "118 18"; + text = "Tags"; + }; + // Create New Tag + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "105 2"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialSelector_addFilterWindow.setVisible(1); MaterialSelectorOverlay.pushToBack(MaterialSelector_addFilterWindow);"; + hovertime = "1000"; + tooltip = "Create New Tag"; + bitmap = "tools/gui/images/new"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "89 2"; + Extent = "13 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialSelector.clearMaterialFilters();"; + hovertime = "1000"; + tooltip = "Clear Selected Tag"; + bitmap = "tools/gui/images/clear-btn"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + }; + + %i = %staticFilterContainer.getCount(); + for( ; %i != 0; %i--) + MaterialSelector-->filterArray.addGuiControl(%staticFilterContainer.getObject(0)); + + MaterialSelector.staticFilterObjects = MaterialSelector-->filterArray.getCount(); + + %staticFilterContainer.delete(); + + // Create our category array used in the selector, this code should be taken out + // in order to make the material selector agnostic + new ArrayObject(MaterialFilterAllArray); + new ArrayObject(MaterialFilterMappedArray); + new ArrayObject(MaterialFilterUnmappedArray); + + %mats = ""; + %count = 0; + if( MaterialSelector.terrainMaterials ) + { + %mats = ETerrainEditor.getTerrainBlocksMaterialList(); + %count = getRecordCount( %mats ); + } + else + { + %count = materialSet.getCount(); + } + + for(%i = 0; %i < %count; %i++) + { + // Process terrain materials + if( MaterialSelector.terrainMaterials ) + { + %matInternalName = getRecord( %mats, %i ); + %material = TerrainMaterialSet.findObjectByInternalName( %matInternalName ); + + // Is there no material info for this slot? + if ( !isObject( %material ) ) + continue; + + // Add to the appropriate filters + MaterialFilterMappedArray.add( "", %material ); + MaterialFilterAllArray.add( "", %material ); + + continue; + } + + // Process regular materials here + %material = materialSet.getObject(%i); + + for( %k = 0; %k < UnlistedMaterials.count(); %k++ ) + { + %unlistedFound = 0; + if( UnlistedMaterials.getValue(%k) $= %material.name ) + { + %unlistedFound = 1; + break; + } + } + + if( %unlistedFound ) + continue; + + if( %material.mapTo $= "" || %material.mapTo $= "unmapped_mat" ) + { + MaterialFilterUnmappedArray.add( "", %material.name ); + //running through the existing tag names + for( %j = 0; %material.getFieldValue("materialTag" @ %j) !$= ""; %j++ ) + MaterialFilterUnmappedArray.add( %material.getFieldValue("materialTag" @ %j), %material.name ); + } + else + { + MaterialFilterMappedArray.add( "", %material.name ); + for( %j = 0; %material.getFieldValue("materialTag" @ %j) !$= ""; %j++ ) + MaterialFilterMappedArray.add( %material.getFieldValue("materialTag" @ %j), %material.name ); + } + + MaterialFilterAllArray.add( "", %material.name ); + for( %j = 0; %material.getFieldValue("materialTag" @ %j) !$= ""; %j++ ) + MaterialFilterAllArray.add( %material.getFieldValue("materialTag" @ %j), %material.name ); + + } + + MaterialFilterAllArrayCheckbox.setText("All ( " @ MaterialFilterAllArray.count() @" ) "); + MaterialFilterMappedArrayCheckbox.setText("Mapped ( " @ MaterialFilterMappedArray.count() @" ) "); + MaterialFilterUnmappedArrayCheckbox.setText("Unmapped ( " @ MaterialFilterUnmappedArray.count() @" ) "); +} + +function MaterialSelector::preloadFilter( %this ) +{ + %selectedFilter = ""; + for( %i = MaterialSelector.staticFilterObjects; %i < MaterialSelector-->filterArray.getCount(); %i++ ) + { + if( MaterialSelector-->filterArray.getObject(%i).getObject(0).getValue() == 1 ) + { + if( %selectedFilter $= "" ) + %selectedFilter = MaterialSelector-->filterArray.getObject(%i).getObject(0).filter; + else + %selectedFilter = %selectedFilter @ " " @ MaterialSelector-->filterArray.getObject(%i).getObject(0).filter; + } + } + MaterialSelector.loadFilter( %selectedFilter ); +} + +function MaterialSelector::loadFilter( %this, %selectedFilter, %staticFilter ) +{ + // manage schedule array properly + if(!isObject(MatEdScheduleArray)) + new ArrayObject(MatEdScheduleArray); + + // if we select another list... delete all schedules that were created by + // previous load + for( %i = 0; %i < MatEdScheduleArray.count(); %i++ ) + cancel(MatEdScheduleArray.getKey(%i)); + + // we have to empty out the list; so when we create new schedules, these dont linger + MatEdScheduleArray.empty(); + + // manage preview array + if(!isObject(MatEdPreviewArray)) + new ArrayObject(MatEdPreviewArray); + + // we have to empty out the list; so when we create new guicontrols, these dont linger + MatEdPreviewArray.empty(); + MaterialSelector-->materialSelection.deleteAllObjects(); + MaterialSelector-->materialPreviewPagesStack.deleteAllObjects(); + + // changed to accomadate tagging. dig through the array for each tag name, + // call unique value, sort, and we have a perfect set of materials + if( %staticFilter !$= "" ) + MaterialSelector.currentStaticFilter = %staticFilter; + + MaterialSelector.currentFilter = %selectedFilter; + + %filteredObjectsArray = new ArrayObject(); + + %previewsPerPage = MaterialSelector-->materialPreviewCountPopup.getTextById( MaterialSelector-->materialPreviewCountPopup.getSelected() ); + + %tagCount = getWordCount( MaterialSelector.currentFilter ); + if( %tagCount != 0 ) + { + for( %j = 0; %j < %tagCount; %j++ ) + { + for( %i = 0; %i < MaterialSelector.currentStaticFilter.count(); %i++ ) + { + %currentTag = getWord( MaterialSelector.currentFilter, %j ); + if( MaterialSelector.currentStaticFilter.getKey(%i) $= %currentTag) + %filteredObjectsArray.add( MaterialSelector.currentStaticFilter.getKey(%i), MaterialSelector.currentStaticFilter.getValue(%i) ); + } + } + + %filteredObjectsArray.uniqueValue(); + %filteredObjectsArray.sortd(); + + MaterialSelector.totalPages = mCeil( %filteredObjectsArray.count() / %previewsPerPage ); + + //Can we maintain the current preview page, or should we go to page 1? + if( (MaterialSelector.currentPreviewPage * %previewsPerPage) >= %filteredObjectsArray.count() ) + MaterialSelector.currentPreviewPage = 0; + + // Build out the pages buttons + MaterialSelector.buildPagesButtons( MaterialSelector.currentPreviewPage, MaterialSelector.totalPages ); + + %previewCount = %previewsPerPage; + %possiblePreviewCount = %filteredObjectsArray.count() - MaterialSelector.currentPreviewPage * %previewsPerPage; + if( %possiblePreviewCount < %previewCount ) + %previewCount = %possiblePreviewCount; + + %start = MaterialSelector.currentPreviewPage * %previewsPerPage; + for( %i = %start; %i < %start + %previewCount; %i++ ) + MaterialSelector.buildPreviewArray( %filteredObjectsArray.getValue(%i) ); + + %filteredObjectsArray.delete(); + } + else + { + MaterialSelector.currentStaticFilter.sortd(); + + // Rebuild the static filter list without tagged materials + %noTagArray = new ArrayObject(); + for( %i = 0; %i < MaterialSelector.currentStaticFilter.count(); %i++ ) + { + if( MaterialSelector.currentStaticFilter.getKey(%i) !$= "") + continue; + + %material = MaterialSelector.currentStaticFilter.getValue(%i); + + // CustomMaterials are not available for selection + if ( !isObject( %material ) || %material.isMemberOfClass( "CustomMaterial" ) ) + continue; + + %noTagArray.add( "", %material ); + } + + MaterialSelector.totalPages = mCeil( %noTagArray.count() / %previewsPerPage ); + + //Can we maintain the current preview page, or should we go to page 1? + if( (MaterialSelector.currentPreviewPage * %previewsPerPage) >= %noTagArray.count() ) + MaterialSelector.currentPreviewPage = 0; + + // Build out the pages buttons + MaterialSelector.buildPagesButtons( MaterialSelector.currentPreviewPage, MaterialSelector.totalPages ); + + %previewCount = %previewsPerPage; + %possiblePreviewCount = %noTagArray.count() - MaterialSelector.currentPreviewPage * %previewsPerPage; + if( %possiblePreviewCount < %previewCount ) + %previewCount = %possiblePreviewCount; + + %start = MaterialSelector.currentPreviewPage * %previewsPerPage; + for( %i = %start; %i < %start + %previewCount; %i++ ) + { + MaterialSelector.buildPreviewArray( %noTagArray.getValue(%i) ); + } + } + + + MaterialSelector.loadImages( 0 ); +} + + + +function MaterialSelector::buildPreviewArray( %this, %material ) +{ + %matName = ""; + + // CustomMaterials are not available for selection + if ( !isObject( %material ) || %material.isMemberOfClass( "CustomMaterial" ) ) + return; + + if( %material.isMemberOfClass("TerrainMaterial") ) + { + %matName = %material.getInternalName(); + + if( %material.diffuseMap $= "") + %previewImage = "core/art/warnmat"; + else + %previewImage = %material.diffuseMap; + } + else if( %material.toneMap[0] $= "" && %material.diffuseMap[0] $= "" && !isObject(%material.cubemap) ) + { + %matName = %material.name; + %previewImage = "core/art/warnmat"; + } + else + { + %matName = %material.name; + + if( %material.toneMap[0] !$= "" ) + %previewImage = %material.toneMap[0]; + else if( %material.diffuseMap[0] !$= "" ) + %previewImage = %material.diffuseMap[0]; + else if( %material.cubemap.cubeFace[0] !$= "" ) + %previewImage = %material.cubemap.cubeFace[0]; + + //%previewImage = MaterialEditorGui.searchForTexture( %material, %previewImage ); + + // were going to use a couple of string commands in order to properly + // find out what the img src path is + // **NEW** this needs to be updated with the above, but has some timing issues + %materialDiffuse = %previewImage; + %materialPath = %material.getFilename(); + + if( strchr( %materialDiffuse, "/") $= "" ) + { + %k = 0; + while( strpos( %materialPath, "/", %k ) != -1 ) + { + %foo = strpos( %materialPath, "/", %k ); + %k = %foo + 1; + } + + %foobar = getSubStr( %materialPath , %k , 99 ); + %previewImage = strreplace( %materialPath, %foobar, %previewImage ); + } + else + %previewImage = strreplace( %materialPath, %materialPath, %previewImage ); + } + + // it may seem goofy why the checkbox can't be instanciated inside the container + // reason being its because we need to store the checkbox ctrl in order to make changes + // on it later in the function. + + %container = new GuiControl(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "74 87"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + + new GuiTextCtrl(){ + position = "7 71"; + profile = "ToolsGuiTextCenterProfile"; + extent = "64 16"; + text = %matName; + }; + }; + + %previewButton = new GuiBitmapButtonCtrl(){ + internalName = %matName; + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + position = "7 4"; + extent = "64 64"; + buttonType = "PushButton"; + bitmap = ""; + Command = ""; + text = "Loading..."; + useStates = false; + + new GuiBitmapButtonCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + position = "0 0"; + extent = "64 64"; + Variable = ""; + buttonType = "toggleButton"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + groupNum = "0"; + text = ""; + }; + }; + + %previewBorder = new GuiButtonCtrl(){ + internalName = %matName@"Border"; + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiThumbHighlightButtonProfile"; + position = "3 0"; + extent = "72 88"; + Variable = ""; + buttonType = "toggleButton"; + tooltip = %matName; + Command = "MaterialSelector.updateSelection( $ThisControl.getParent().getObject(1).internalName, $ThisControl.getParent().getObject(1).bitmap );"; + groupNum = "0"; + text = ""; + }; + + %container.add(%previewButton); + %container.add(%previewBorder); + // add to the gui control array + MaterialSelector-->materialSelection.add(%container); + + // add to the array object for reference later + MatEdPreviewArray.add( %previewButton, %previewImage ); +} + +function MaterialSelector::loadImages( %this, %materialNum ) +{ + // this will save us from spinning our wheels in case we don't exist + if( !MaterialSelector.visible ) + return; + + // this schedule is here to dynamically load images + %previewButton = MatEdPreviewArray.getKey(%materialNum); + %previewImage = MatEdPreviewArray.getValue(%materialNum); + + %previewButton.setBitmap(%previewImage); + %previewButton.setText(""); + + %materialNum++; + + if( %materialNum < MatEdPreviewArray.count() ) + { + %tempSchedule = %this.schedule(64, "loadImages", %materialNum); + MatEdScheduleArray.add( %tempSchedule, %materialNum ); + } +} + +function MaterialSelector::clearMaterialFilters( %this ) +{ + for( %i = MaterialSelector.staticFilterObjects; %i < MaterialSelector-->filterArray.getCount(); %i++ ) + MaterialSelector-->filterArray.getObject(%i).getObject(0).setStateOn(0); + + MaterialSelector.loadFilter( "", "" ); +} + +function MaterialSelector::loadMaterialFilters( %this ) +{ + %filteredTypesArray = new ArrayObject(); + + %filteredTypesArray.duplicate( MaterialFilterAllArray ); + %filteredTypesArray.uniqueKey(); + + // sort the the keys before we do anything + %filteredTypesArray.sortkd(); + + eval( MaterialSelector.currentStaticFilter @ "Checkbox.setStateOn(1);" ); + // it may seem goofy why the checkbox can't be instanciated inside the container + // reason being its because we need to store the checkbox ctrl in order to make changes + // on it later in the function. + %selectedFilter = ""; + for( %i = 0; %i < %filteredTypesArray.count(); %i++ ) + { + %filter = %filteredTypesArray.getKey(%i); + if(%filter $= "") + continue; + + %container = new GuiControl(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + }; + + %checkbox = new GuiCheckBoxCtrl(){ + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 1"; + Extent = "118 18"; + Command = ""; + groupNum = "0"; + buttonType = "ToggleButton"; + text = %filter @ " ( " @ MaterialFilterAllArray.countKey(%filter) @ " )"; + filter = %filter; + Command = "MaterialSelector.preloadFilter();"; + }; + %container.add( %checkbox ); + MaterialSelector-->filterArray.add( %container ); + + %tagCount = getWordCount( MaterialSelector.currentFilter ); + for( %j = 0; %j < %tagCount; %j++ ) + { + if( %filter $= getWord( MaterialSelector.currentFilter, %j ) ) + { + if( %selectedFilter $= "" ) + %selectedFilter = %filter; + else + %selectedFilter = %selectedFilter @ " " @ %filter; + + %checkbox.setStateOn(1); + } + } + } + + MaterialSelector.loadFilter( %selectedFilter ); + + %filteredTypesArray.delete(); +} + +// create category and update current material if there is one +function MaterialSelector::createFilter( %this, %filter ) +{ + if( %filter $= %existingFilters ) + { + MessageBoxOK( "Error", "Can not create blank filter."); + return; + } + + for( %i = MaterialSelector.staticFilterObjects; %i < MaterialSelector-->filterArray.getCount() ; %i++ ) + { + %existingFilters = MaterialSelector-->filterArray.getObject(%i).getObject(0).filter; + if( %filter $= %existingFilters ) + { + MessageBoxOK( "Error", "Can not create two filters of the same name."); + return; + } + } + %container = new GuiControl(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "128 18"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + + new GuiCheckBoxCtrl(){ + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 1"; + Extent = "118 18"; + Command = ""; + groupNum = "0"; + buttonType = "ToggleButton"; + text = %filter @ " ( " @ MaterialFilterAllArray.countKey(%filter) @ " )"; + filter = %filter; + Command = "MaterialSelector.preloadFilter();"; + }; + }; + + MaterialSelector-->filterArray.add( %container ); + + // if selection exists, lets reselect it to refresh it + if( isObject(MaterialSelector.selectedMaterial) ) + MaterialSelector.updateSelection( MaterialSelector.selectedMaterial, MaterialSelector.selectedPreviewImagePath ); + + // material category text field to blank + MaterialSelector_addFilterWindow-->tagName.setText(""); +} + +function MaterialSelector::updateSelection( %this, %material, %previewImagePath ) +{ + // the material selector will visually update per material information + // after we move away from the material. eg: if we remove a field from the material, + // the empty checkbox will still be there until you move fro and to the material again + + %isMaterialBorder = 0; + eval("%isMaterialBorder = isObject(MaterialSelector-->"@%material@"Border);"); + if( %isMaterialBorder ) + { + eval( "MaterialSelector-->"@%material@"Border.setStateOn(1);"); + } + + %isMaterialBorderPrevious = 0; + eval("%isMaterialBorderPrevious = isObject(MaterialSelector-->"@$prevSelectedMaterialHL@"Border);"); + if( %isMaterialBorderPrevious ) + { + eval( "MaterialSelector-->"@$prevSelectedMaterialHL@"Border.setStateOn(0);"); + } + + MaterialSelector-->materialCategories.deleteAllObjects(); + MaterialSelector.selectedMaterial = %material; + MaterialSelector.selectedPreviewImagePath = %previewImagePath; + MaterialSelector-->previewSelectionText.setText( %material ); + MaterialSelector-->previewSelection.setBitmap( %previewImagePath ); + + // running through the existing list of categorynames in the left, so yes + // some might exist on the left only temporary if not given a home + for( %i = MaterialSelector.staticFilterObjects; %i < MaterialSelector-->filterArray.getCount() ; %i++ ) + { + %filter = MaterialSelector-->filterArray.getObject(%i).getObject(0).filter; + + %checkbox = new GuiCheckBoxCtrl(){ + materialName = %material.name; + Profile = "ToolsGuiCheckBoxListProfile"; + position = "5 2"; + Extent = "118 18"; + Command = "MaterialSelector.updateMaterialTags( $ThisControl.materialName, $ThisControl.getText(), $ThisControl.getValue() );"; + text = %filter; + }; + + MaterialSelector-->materialCategories.add( %checkbox ); + // crawl through material for categories in order to check or not + %filterFound = 0; + for( %j = 0; %material.getFieldValue("materialTag" @ %j) !$= ""; %j++ ) + { + %tag = %material.getFieldValue("materialTag" @ %j); + + if( %tag $= %filter ) + { + %filterFound = 1; + break; + } + } + + if( %filterFound ) + %checkbox.setStateOn(1); + else + %checkbox.setStateOn(0); + } + + $prevSelectedMaterialHL = %material; +} + +function MaterialSelector::updateMaterialTags( %this, %material, %tag, %tagValue ) +{ + if( %tagValue == 1 ) + { + MaterialFilterAllArray.add( %tag, %material ); + if( %material.mapTo $= "" || %material.mapTo $= "unmapped_mat" ) + %secondStaticFilter = MaterialFilterUnmappedArray; + else + %secondStaticFilter = MaterialFilterMappedArray; + + %secondStaticFilter.add( %tag, %material ); + + %createdTag = 0; + for( %i = 0; %createdTag == 0; %i++ ) + { + %materialTag = %material.getFieldValue("materialTag" @ %i); + if( %materialTag $= "" ) + { + eval( %material @ ".materialTag" @ %i @ "=" @ %tag @ ";" ); + %createdTag = 1; + + for( %j = MaterialSelector.staticFilterObjects; %j < MaterialSelector-->filterArray.getCount() ; %j++ ) + { + if( %tag $= MaterialSelector-->filterArray.getObject(%j).getObject(0).filter ) + { + %count = getWord( MaterialSelector-->filterArray.getObject(%j).getObject(0).getText(), 2 ); + %count++; + MaterialSelector-->filterArray.getObject(%j).getObject(0).setText( %tag @ " ( "@ %count @ " )"); + } + } + + break; + } + } + + } + else + { + // Remove the material from the "all" category + for( %i = 0; %i < MaterialFilterAllArray.count(); %i++ ) + { + if( MaterialFilterAllArray.getKey(%i) $= %tag ) + { + if( MaterialFilterAllArray.getValue(%i) $= %material ) + { + MaterialFilterAllArray.erase(%i); + break; + } + } + } + + // Figure out what the material's other category is + if( %material.mapTo $= "" || %material.mapTo $= "unmapped_mat" ) + %secondStaticFilter = MaterialFilterUnmappedArray; + else + %secondStaticFilter = MaterialFilterMappedArray; + + // Remove the material from its other category + for( %i = 0; %i < %secondStaticFilter.count(); %i++ ) + { + if( %secondStaticFilter.getKey(%i) $= %tag ) + { + if( %secondStaticFilter.getValue(%i) $= %material ) + { + %secondStaticFilter.erase( %i ); + break; + } + } + } + + + MaterialSelector.updateFilterCount( %tag, false ); + + %tagField = MaterialSelector.getTagField( %material, %tag ); + %lastTagField = MaterialSelector.getLastTagField( %material ); + %lastValidTagField = MaterialSelector.getLastValidTagField( %material, %tag ); + + if( %tagField $= %lastValidTagField || %lastValidTagField $= "" ) + { + MaterialSelectorPerMan.removeField( %material, %tagField ); + } + else + { + // Replace the current tagFieldValue with the last tagFieldValue + %lastValidTag = %material.getFieldValue( %lastValidTagField ); + %material.setFieldValue( %tagField, %lastValidTag ); + + // Remove the last tagFieldValue + MaterialSelectorPerMan.removeField( %material, %lastTagField ); + } + } + + // so were not going to save materials that dont current exist... + // technically all the data is stored in dynamic fields if the user feels like saving + // their auto-generated or new material + if( %material.getFilename() !$= "" && + %material.getFilename() !$= "tools/gui/MaterialSelector.ed.gui" && + %material.getFilename() !$= "tools/materialEditor/scripts/materialEditor.ed.cs" ) + { + MaterialSelectorPerMan.setDirty( %material ); + MaterialSelectorPerMan.saveDirty(); + MaterialSelectorPerMan.removeDirty( %material ); + + if(!%tagValue) + %material.setFieldValue( %lastTagField, "" ); + } +} + +function MaterialSelector::updateFilterCount( %this, %tag, %add ) +{ + for( %i = MaterialSelector.staticFilterObjects; %i < MaterialSelector-->filterArray.getCount() ; %i++ ) + { + if( %tag $= MaterialSelector-->filterArray.getObject(%i).getObject(0).filter ) + { + // Get the filter count and apply the operation + %idx = getWord( MaterialSelector-->filterArray.getObject(%i).getObject(0).getText(), 2 ); + + if( %add ) + %idx++; + else + %idx--; + + MaterialSelector-->filterArray.getObject(%i).getObject(0).setText( %tag @ " ( "@ %idx @ " )"); + } + } +} + +// this should create a new material pretty nicely +function MaterialSelector::createNewMaterial( %this ) +{ + // look for a newMaterial name to grab + %material = getUniqueName( "newMaterial" ); + + new Material(%material) + { + diffuseMap[0] = "core/art/warnMat"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + + // add one to All filter + MaterialFilterAllArray.add( "", %material.name ); + MaterialFilterAllArrayCheckbox.setText("All ( " @ MaterialFilterAllArray.count() + 1 @ " ) "); + + MaterialFilterUnmappedArray.add( "", %material.name ); + MaterialFilterUnmappedArrayCheckbox.setText("Unmapped ( " @ MaterialFilterUnmappedArray.count() + 1 @ " ) "); + + if( MaterialSelector.currentStaticFilter !$= "MaterialFilterMappedArray" ) + { + // create the new material gui + %container = new GuiControl(){ + profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "74 85"; + HorizSizing = "right"; + VertSizing = "bottom"; + isContainer = "1"; + new GuiTextCtrl(){ + position = "10 70"; + profile = "ToolsGuiTextCenterProfile"; + extent = "64 16"; + text = %material.name; + }; + }; + + %previewButton = new GuiBitmapButtonCtrl(){ + internalName = %material.name; + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + position = "7 4"; + extent = "64 64"; + buttonType = "PushButton"; + bitmap = "core/art/warnMat"; + Command = ""; + text = "Loading..."; + useStates = false; + + new GuiBitmapButtonCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + position = "0 0"; + extent = "64 64"; + Variable = ""; + buttonType = "toggleButton"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + groupNum = "0"; + text = ""; + }; + }; + + %previewBorder = new GuiButtonCtrl(){ + internalName = %material.name@"Border"; + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiThumbHighlightButtonProfile"; + position = "3 0"; + extent = "72 88"; + Variable = ""; + buttonType = "toggleButton"; + tooltip = %material.name; + Command = "MaterialSelector.updateSelection( $ThisControl.getParent().getObject(1).internalName, $ThisControl.getParent().getObject(1).bitmap );"; + groupNum = "0"; + text = ""; + }; + + %container.add(%previewButton); + %container.add(%previewBorder); + // add to the gui control array + MaterialSelector-->materialSelection.add(%container); + } + + // select me + MaterialSelector.updateSelection( %material, "core/art/warnMat.png" ); +} + +//needs to be deleted with the persistence manager and needs to be blanked out of the matmanager +//also need to update instances... i guess which is the tricky part.... +function MaterialSelector::showDeleteDialog( %this ) +{ + %material = MaterialSelector.selectedMaterial; + %secondFilter = "MaterialFilterMappedArray"; + %secondFilterName = "Mapped"; + + for( %i = 0; %i < MaterialFilterUnmappedArray.count(); %i++ ) + { + if( MaterialFilterUnmappedArray.getValue(%i) $= %material ) + { + %secondFilter = "MaterialFilterUnmappedArray"; + %secondFilterName = "Unmapped"; + break; + } + } + + if( isObject( %material ) ) + { + MessageBoxYesNoCancel("Delete Material?", + "Are you sure you want to delete<br><br>" @ %material.getName() @ "<br><br> Material deletion won't take affect until the engine is quit.", + "MaterialSelector.deleteMaterial( " @ %material @ ", " @ %secondFilter @ ", " @ %secondFilterName @" );", + "", + "" ); + } +} + +function MaterialSelector::deleteMaterial( %this, %materialName, %secondFilter, %secondFilterName ) +{ + if( !isObject( %materialName ) ) + return; + + for( %i = 0; %i <= MaterialFilterAllArray.countValue( %materialName ); %i++) + { + %index = MaterialFilterAllArray.getIndexFromValue( %materialName ); + MaterialFilterAllArray.erase( %index ); + } + MaterialFilterAllArrayCheckbox.setText("All ( " @ MaterialFilterAllArray.count() - 1 @ " ) "); + + %checkbox = %secondFilter @ "Checkbox"; + for( %k = 0; %k <= %secondFilter.countValue( %materialName ); %k++) + { + %index = %secondFilter.getIndexFromValue( %materialName ); + %secondFilter.erase( %index ); + } + %checkbox.setText( %secondFilterName @ " ( " @ %secondFilter.count() - 1 @ " ) "); + + for( %i = 0; %materialName.getFieldValue("materialTag" @ %i) !$= ""; %i++ ) + { + %materialTag = %materialName.getFieldValue("materialTag" @ %i); + + for( %j = MaterialSelector.staticFilterObjects; %j < MaterialSelector-->filterArray.getCount() ; %j++ ) + { + if( %materialTag $= MaterialSelector-->filterArray.getObject(%j).getObject(0).filter ) + { + %count = getWord( MaterialSelector-->filterArray.getObject(%j).getObject(0).getText(), 2 ); + %count--; + MaterialSelector-->filterArray.getObject(%j).getObject(0).setText( %materialTag @ " ( "@ %count @ " )"); + } + } + + } + + UnlistedMaterials.add( "unlistedMaterials", %materialName ); + + if( %materialName.getFilename() !$= "" && + %materialName.getFilename() !$= "tools/gui/MaterialSelector.ed.gui" && + %materialName.getFilename() !$= "tools/materialEditor/scripts/materialEditor.ed.cs" ) + { + MaterialSelectorPerMan.removeObjectFromFile(%materialName); + MaterialSelectorPerMan.saveDirty(); + } + + MaterialSelector.preloadFilter(); + //MaterialSelector.selectMaterial( "WarningMaterial" ); +} + +function MaterialSelector::switchStaticFilters( %this, %staticFilter) +{ + switch$(%staticFilter) + { + case "MaterialFilterAllArray": + MaterialFilterAllArrayCheckbox.setStateOn(1); + + MaterialFilterMappedArrayCheckbox.setStateOn(0); + MaterialFilterUnmappedArrayCheckbox.setStateOn(0); + case "MaterialFilterMappedArray": + MaterialFilterMappedArrayCheckbox.setStateOn(1); + + MaterialFilterAllArrayCheckbox.setStateOn(0); + MaterialFilterUnmappedArrayCheckbox.setStateOn(0); + case "MaterialFilterUnmappedArray": + MaterialFilterUnmappedArrayCheckbox.setStateOn(1); + + MaterialFilterAllArrayCheckbox.setStateOn(0); + MaterialFilterMappedArrayCheckbox.setStateOn(0); + } + + // kinda goofy were passing a class variable... we can't do an empty check right now + // on load filter because we actually pass "" as a filter... + MaterialSelector.loadFilter( MaterialSelector.currentFilter, %staticFilter ); +} + +// Tagging Functionality + +function MaterialSelector::getTagField( %this, %material, %tag ) +{ + for( %i = 0; %material.getFieldValue("materialTag" @ %i) !$= ""; %i++ ) + { + %loopTag = %material.getFieldValue("materialTag" @ %i); + if( %tag $= %loopTag ) + { + %tagField = "materialTag" @ %i; + break; + } + } + + return %tagField; +} + +function MaterialSelector::getLastTagField( %this, %material ) +{ + for( %i = 0; %material.getFieldValue("materialTag" @ %i) !$= ""; %i++ ) + { + %tagField = "materialTag" @ %i; + } + + return %tagField; +} + +function MaterialSelector::getLastValidTagField( %this, %material, %invalidTag ) +{ + for( %i = 0; %material.getFieldValue("materialTag" @ %i) !$= ""; %i++ ) + { + %tag = %material.getFieldValue("materialTag" @ %i); + // Can't equal our invalid tag + if( %tag $= %invalidTag ) + continue; + + // Set our last found tag + %tagField = "materialTag" @ %i; + } + + return %tagField; +} + +// Preview Page Navigation + +function MaterialSelector::firstPage(%this) +{ + MaterialSelector.currentPreviewPage = 0; + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::previousPage(%this) +{ + MaterialSelector.currentPreviewPage--; + if( MaterialSelector.currentPreviewPage < 0) + MaterialSelector.currentPreviewPage = 0; + + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::nextPage(%this) +{ + MaterialSelector.currentPreviewPage++; + if( MaterialSelector.currentPreviewPage >= MaterialSelector.totalPages) + MaterialSelector.currentPreviewPage = MaterialSelector.totalPages - 1; + if( MaterialSelector.currentPreviewPage < 0) + MaterialSelector.currentPreviewPage = 0; + + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::lastPage(%this) +{ + MaterialSelector.currentPreviewPage = MaterialSelector.totalPages - 1; + if( MaterialSelector.currentPreviewPage < 0) + MaterialSelector.currentPreviewPage = 0; + + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::selectPage(%this, %page) +{ + MaterialSelector.currentPreviewPage = %page; + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::thumbnailCountUpdate(%this) +{ + $Pref::MaterialSelector::ThumbnailCountIndex = MaterialSelector-->materialPreviewCountPopup.getSelected(); + MaterialSelector.LoadFilter( MaterialSelector.currentFilter, MaterialSelector.currentStaticFilter ); +} + +function MaterialSelector::buildPagesButtons(%this, %currentPage, %totalPages) +{ + // We don't want any more than 8 pages at a time. + if( %totalPages > 8 ) + { + // We attempt to display up to 2 pages before the current page + %start = %currentPage - 2; + if( %start <= 0 ) + { + %start = 0; + %startbracket = false; + } + else + { + %startbracket = true; + } + + if( (%totalPages - %start) < 8 ) + { + // Move %start closer to the beginning to maintain 8 pages + %start = %totalPages - 8; + } + + %end = %start + 8; + if( %end >= %totalPages ) + { + %end = %totalPages; + %endbracket = false; + } + else + { + %endbracket = true; + } + } + else + { + %start = 0; + %end = %totalPages; + %startbracket = false; + %endbracket = false; + } + + if( %startbracket ) + { + %control = new GuiTextCtrl(){ + profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + extent = "14 16"; + MinExtent = "8 8"; + text = "..."; + }; + MaterialSelector-->materialPreviewPagesStack.add( %control ); + } + + for( %i = %start; %i < %end; %i++ ) + { + if( %i != %currentPage ) + { + %control = new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "14 16"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "MaterialSelector.schedule(0, selectPage, " @ %i @ ");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = %i+1; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + } + else + { + %control = new GuiTextCtrl(){ + profile = "ToolsGuiTextBoldCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + extent = "14 16"; + MinExtent = "8 8"; + text = %i+1; + }; + } + + MaterialSelector-->materialPreviewPagesStack.add( %control ); + } + + if( %endbracket ) + { + %control = new GuiTextCtrl(){ + profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + extent = "14 16"; + MinExtent = "8 8"; + text = "..."; + }; + MaterialSelector-->materialPreviewPagesStack.add( %control ); + } +} diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBox.ed.cs b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBox.ed.cs new file mode 100644 index 000000000..95592c016 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBox.ed.cs @@ -0,0 +1,325 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// Cleanup Dialog created by 'core' +if( isObject( MessagePopupDlg ) ) + MessagePopupDlg.delete(); +if( isObject( MessageBoxYesNoDlg ) ) + MessageBoxYesNoDlg.delete(); +if( isObject( MessageBoxYesNoCancelDlg ) ) + MessageBoxYesNoCancelDlg.delete(); +if( isObject( MessageBoxOKCancelDetailsDlg ) ) + MessageBoxOKCancelDetailsDlg.delete(); +if( isObject( MessageBoxOKCancelDlg ) ) + MessageBoxOKCancelDlg.delete(); +if( isObject( MessageBoxOKDlg ) ) + MessageBoxOKDlg.delete(); +if( isObject( IODropdownDlg ) ) + IODropdownDlg.delete(); + + +// Load Editor Dialogs +exec("./messageBoxOK.ed.gui"); +exec("./messageBoxYesNo.ed.gui"); +exec("./messageBoxYesNoCancel.ed.gui"); +exec("./messageBoxOKCancel.ed.gui"); +exec("./messageBoxOKCancelDetailsDlg.ed.gui"); +exec("./messagePopup.ed.gui"); + + + +// -------------------------------------------------------------------- +// Message Sound +// -------------------------------------------------------------------- +/*new SFXDescription(MessageBoxAudioDescription) +{ + volume = 1.0; + isLooping = false; + is3D = false; + channel = $GuiAudioType; +}; + +new SFXProfile(messageBoxBeep) +{ + filename = "./messageBoxSound"; + description = MessageBoxAudioDescription; + preload = true; +};*/ + + + + +//--------------------------------------------------------------------------------------------- +// messageCallback +// Calls a callback passed to a message box. +//--------------------------------------------------------------------------------------------- +function messageCallback(%dlg, %callback) +{ + Canvas.popDialog(%dlg); + eval(%callback); +} + +//The # in the function passed replaced with the output +//of the preset menu. +function IOCallback(%dlg, %callback) +{ + %id = IODropdownMenu.getSelected(); + %text = IODropdownMenu.getTextById(%id); + %callback = strreplace(%callback, "#", %text); + eval(%callback); + + Canvas.popDialog(%dlg); +} + +//--------------------------------------------------------------------------------------------- +// MBSetText +// Sets the text of a message box and resizes it to accomodate the new string. +//--------------------------------------------------------------------------------------------- +function MBSetText(%text, %frame, %msg) +{ + // Get the extent of the text box. + %ext = %text.getExtent(); + // Set the text in the center of the text box. + %text.setText("<just:center>" @ %msg); + // Force the textbox to resize itself vertically. + %text.forceReflow(); + // Grab the new extent of the text box. + %newExtent = %text.getExtent(); + + // Get the vertical change in extent. + %deltaY = getWord(%newExtent, 1) - getWord(%ext, 1); + + // Resize the window housing the text box. + %windowPos = %frame.getPosition(); + %windowExt = %frame.getExtent(); + %frame.resize(getWord(%windowPos, 0), getWord(%windowPos, 1) - (%deltaY / 2), + getWord(%windowExt, 0), getWord(%windowExt, 1) + %deltaY); + + %frame.canMove = "0"; + //%frame.canClose = "0"; + %frame.resizeWidth = "0"; + %frame.resizeHeight = "0"; + %frame.canMinimize = "0"; + %frame.canMaximize = "0"; + + //sfxPlayOnce( messageBoxBeep ); +} + +//--------------------------------------------------------------------------------------------- +// Various message box display functions. Each one takes a window title, a message, and a +// callback for each button. +//--------------------------------------------------------------------------------------------- + +function MessageBoxOK(%title, %message, %callback) +{ + MBOKFrame.text = %title; + Canvas.pushDialog(MessageBoxOKDlg); + MBSetText(MBOKText, MBOKFrame, %message); + MessageBoxOKDlg.callback = %callback; +} + +function MessageBoxOKDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxOKCancel(%title, %message, %callback, %cancelCallback) +{ + MBOKCancelFrame.text = %title; + Canvas.pushDialog(MessageBoxOKCancelDlg); + MBSetText(MBOKCancelText, MBOKCancelFrame, %message); + MessageBoxOKCancelDlg.callback = %callback; + MessageBoxOKCancelDlg.cancelCallback = %cancelCallback; +} + +function MessageBoxOKCancelDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxOKCancelDetails(%title, %message, %details, %callback, %cancelCallback) +{ + if(%details $= "") + { + MBOKCancelDetailsButton.setVisible(false); + } + + MBOKCancelDetailsScroll.setVisible(false); + + MBOKCancelDetailsFrame.setText( %title ); + + Canvas.pushDialog(MessageBoxOKCancelDetailsDlg); + MBSetText(MBOKCancelDetailsText, MBOKCancelDetailsFrame, %message); + MBOKCancelDetailsInfoText.setText(%details); + + %textExtent = MBOKCancelDetailsText.getExtent(); + %textExtentY = getWord(%textExtent, 1); + %textPos = MBOKCancelDetailsText.getPosition(); + %textPosY = getWord(%textPos, 1); + + %extentY = %textPosY + %textExtentY + 65; + + MBOKCancelDetailsInfoText.setExtent(285, 128); + + MBOKCancelDetailsFrame.setExtent(300, %extentY); + + MessageBoxOKCancelDetailsDlg.callback = %callback; + MessageBoxOKCancelDetailsDlg.cancelCallback = %cancelCallback; + + MBOKCancelDetailsFrame.defaultExtent = MBOKCancelDetailsFrame.getExtent(); +} + +function MBOKCancelDetailsToggleInfoFrame() +{ + if(!MBOKCancelDetailsScroll.isVisible()) + { + MBOKCancelDetailsScroll.setVisible(true); + MBOKCancelDetailsText.forceReflow(); + %textExtent = MBOKCancelDetailsText.getExtent(); + %textExtentY = getWord(%textExtent, 1); + %textPos = MBOKCancelDetailsText.getPosition(); + %textPosY = getWord(%textPos, 1); + + %verticalStretch = %textExtentY; + + if((%verticalStretch > 260) || (%verticalStretch < 0)) + %verticalStretch = 260; + + %extent = MBOKCancelDetailsFrame.defaultExtent; + %height = getWord(%extent, 1); + + %posY = %textPosY + %textExtentY + 10; + %posX = getWord(MBOKCancelDetailsScroll.getPosition(), 0); + MBOKCancelDetailsScroll.setPosition(%posX, %posY); + MBOKCancelDetailsScroll.setExtent(getWord(MBOKCancelDetailsScroll.getExtent(), 0), %verticalStretch); + MBOKCancelDetailsFrame.setExtent(300, %height + %verticalStretch + 10); + } else + { + %extent = MBOKCancelDetailsFrame.defaultExtent; + %width = getWord(%extent, 0); + %height = getWord(%extent, 1); + MBOKCancelDetailsFrame.setExtent(%width, %height); + MBOKCancelDetailsScroll.setVisible(false); + } +} + +function MessageBoxOKCancelDetailsDlg::onSleep( %this ) +{ + %this.callback = ""; +} + +function MessageBoxYesNo(%title, %message, %yesCallback, %noCallback) +{ + MBYesNoFrame.text = %title; + MessageBoxYesNoDlg.profile = "GuiOverlayProfile"; + Canvas.pushDialog(MessageBoxYesNoDlg); + MBSetText(MBYesNoText, MBYesNoFrame, %message); + MessageBoxYesNoDlg.yesCallBack = %yesCallback; + MessageBoxYesNoDlg.noCallback = %noCallBack; +} + +function MessageBoxYesNoCancel(%title, %message, %yesCallback, %noCallback, %cancelCallback) +{ + MBYesNoCancelFrame.text = %title; + MessageBoxYesNoDlg.profile = "GuiOverlayProfile"; + Canvas.pushDialog(MessageBoxYesNoCancelDlg); + MBSetText(MBYesNoCancelText, MBYesNoCancelFrame, %message); + MessageBoxYesNoCancelDlg.yesCallBack = %yesCallback; + MessageBoxYesNoCancelDlg.noCallback = %noCallBack; + MessageBoxYesNoCancelDlg.cancelCallback = %cancelCallback; +} + +function MessageBoxYesNoDlg::onSleep( %this ) +{ + %this.yesCallback = ""; + %this.noCallback = ""; +} + +//--------------------------------------------------------------------------------------------- +// MessagePopup +// Displays a message box with no buttons. Disappears after %delay milliseconds. +//--------------------------------------------------------------------------------------------- +function MessagePopup(%title, %message, %delay) +{ + // Currently two lines max. + MessagePopFrame.setText(%title); + Canvas.pushDialog(MessagePopupDlg); + MBSetText(MessagePopText, MessagePopFrame, %message); + if (%delay !$= "") + schedule(%delay, 0, CloseMessagePopup); +} + +//--------------------------------------------------------------------------------------------- +// IODropdown +// By passing in a simgroup or simset, the user will be able to choose a child of that group +// through a guiPopupMenuCtrl +//--------------------------------------------------------------------------------------------- + +function IODropdown(%title, %message, %simgroup, %callback, %cancelCallback) +{ + IODropdownFrame.text = %title; + Canvas.pushDialog(IODropdownDlg); + MBSetText(IODropdownText, IODropdownFrame, %message); + + if(isObject(%simgroup)) + { + for(%i = 0; %i < %simgroup.getCount(); %i++) + IODropdownMenu.add(%simgroup.getObject(%i).getName()); + + } + + IODropdownMenu.sort(); + IODropdownMenu.setFirstSelected(0); + + IODropdownDlg.callback = %callback; + IODropdownDlg.cancelCallback = %cancelCallback; +} + +function IODropdownDlg::onSleep( %this ) +{ + %this.callback = ""; + %this.cancelCallback = ""; + IODropdownMenu.clear(); +} + +function CloseMessagePopup() +{ + Canvas.popDialog(MessagePopupDlg); +} + +//--------------------------------------------------------------------------------------------- +// "Old" message box function aliases for backwards-compatibility. +//--------------------------------------------------------------------------------------------- + +function MessageBoxOKOld( %title, %message, %callback ) +{ + MessageBoxOK( %title, %message, %callback ); +} +function MessageBoxOKCancelOld( %title, %message, %callback, %cancelCallback ) +{ + MessageBoxOKCancel( %title, %message, %callback, %cancelCallback ); +} +function MessageBoxYesNoOld( %title, %message, %yesCallback, %noCallback ) +{ + MessageBoxYesNo( %title, %message, %yesCallback, %noCallback ); +} diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOK.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOK.ed.gui new file mode 100644 index 000000000..52e119ea6 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOK.ed.gui @@ -0,0 +1,60 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxOKDlg) { + profile = "GuiOverlayProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MBOKFrame) { + profile = "GuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 107"; + minExtent = "48 95"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + + new GuiMLTextCtrl(MBOKText) { + profile = "GuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "9 35"; + extent = "281 24"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "111 75"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKDlg,MessageBoxOKDlg.callback);"; + accelerator = "return"; + helpTag = "0"; + text = "Ok"; + simpleStyle = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKBuy.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKBuy.ed.gui new file mode 100644 index 000000000..87dd913e0 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKBuy.ed.gui @@ -0,0 +1,85 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxOKBuyDlg) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MBOKBuyFrame) { + profile = "ToolsGuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 100"; + minExtent = "48 92"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + closeCommand = "MessageCallback(MessageBoxOKBuyDlg,MessageBoxOKBuyDlg.noCallback);"; + + new GuiMLTextCtrl(MBOKBuyText) { + profile = "ToolsGuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "11 38"; + extent = "280 14"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "70 68"; + extent = "80 22"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKBuyDlg,MessageBoxOKBuyDlg.OKCallback);"; + accelerator = "return"; + helpTag = "0"; + text = "OK"; + simpleStyle = "0"; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "167 68"; + extent = "80 22"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKBuyDlg,MessageBoxOKBuyDlg.BuyCallback);"; + accelerator = "escape"; + helpTag = "0"; + text = "Buy Now!"; + simpleStyle = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function MessageBoxOKBuy(%title, %message, %OKCallback, %BuyCallback) +{ + MBOKBuyFrame.text = %title; + MessageBoxOKBuyDlg.profile = "ToolsGuiOverlayProfile"; + Canvas.pushDialog(MessageBoxOKBuyDlg); + MBSetText(MBOKBuyText, MBOKBuyFrame, %message); + MessageBoxOKBuyDlg.OKCallback = %OKCallback; + MessageBoxOKBuyDlg.BuyCallback = %BuyCallback; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancel.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancel.ed.gui new file mode 100644 index 000000000..5f9c8f5dc --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancel.ed.gui @@ -0,0 +1,75 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxOKCancelDlg) { + profile = "GuiOverlayProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MBOKCancelFrame) { + profile = "GuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 100"; + minExtent = "48 92"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + + new GuiMLTextCtrl(MBOKCancelText) { + profile = "GuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "8 34"; + extent = "283 24"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "66 68"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKCancelDlg,MessageBoxOKCancelDlg.callback);"; + accelerator = "return"; + helpTag = "0"; + text = "Ok"; + simpleStyle = "0"; + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "156 68"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxOKCancelDlg,MessageBoxOKCancelDlg.cancelCallback);"; + accelerator = "escape"; + helpTag = "0"; + text = "Cancel"; + simpleStyle = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancelDetailsDlg.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancelDetailsDlg.ed.gui new file mode 100644 index 000000000..bf746ea09 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxOKCancelDetailsDlg.ed.gui @@ -0,0 +1,137 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxOKCancelDetailsDlg) { + canSaveDynamicFields = "0"; + Profile = "GuiOverlayProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(MBOKCancelDetailsFrame) { + canSaveDynamicFields = "0"; + Profile = "GuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "362 219"; + Extent = "300 330"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + + new GuiMLTextCtrl(MBOKCancelDetailsText) { + canSaveDynamicFields = "0"; + Profile = "GuiMLTextProfile"; + HorizSizing = "center"; + VertSizing = "bottom"; + position = "32 39"; + Extent = "236 70"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "158 273"; + Extent = "110 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MessageCallback(MessageBoxOKCancelDetailsDlg,MessageBoxOKCancelDetailsDlg.callback);"; + Accelerator = "return"; + hovertime = "1000"; + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "30 273"; + Extent = "110 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MessageCallback(MessageBoxOKCancelDetailsDlg,MessageBoxOKCancelDetailsDlg.cancelCallback);"; + Accelerator = "escape"; + hovertime = "1000"; + text = "CANCEL"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiButtonCtrl(MBOKCancelDetailsButton) { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "9 302"; + Extent = "86 17"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MBOKCancelDetailsToggleInfoFrame();"; + hovertime = "1000"; + text = "Details"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiScrollCtrl(MBOKCancelDetailsScroll) { + canSaveDynamicFields = "0"; + Profile = "GuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 115"; + Extent = "281 138"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiMLTextCtrl(MBOKCancelDetailsInfoText) { + canSaveDynamicFields = "0"; + Profile = "GuiMLTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "259 56"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNo.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNo.ed.gui new file mode 100644 index 000000000..3cd7d18ef --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNo.ed.gui @@ -0,0 +1,75 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxYesNoDlg) { + profile = "GuiOverlayProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MBYesNoFrame) { + profile = "GuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 100"; + minExtent = "48 92"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + closeCommand = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.noCallback);"; + + new GuiMLTextCtrl(MBYesNoText) { + profile = "GuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "11 38"; + extent = "280 14"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "70 68"; + extent = "80 22"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.yesCallback);"; + accelerator = "return"; + helpTag = "0"; + text = "Yes"; + simpleStyle = "0"; + }; + new GuiButtonCtrl() { + profile = "GuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "167 68"; + extent = "80 22"; + minExtent = "8 8"; + visible = "1"; + command = "MessageCallback(MessageBoxYesNoDlg,MessageBoxYesNoDlg.noCallback);"; + accelerator = "escape"; + helpTag = "0"; + text = "No"; + simpleStyle = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNoCancel.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNoCancel.ed.gui new file mode 100644 index 000000000..03257494d --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messageBoxYesNoCancel.ed.gui @@ -0,0 +1,103 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxYesNoCancelDlg) { + canSaveDynamicFields = "0"; + Profile = "GuiOverlayProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(MBYesNoCancelFrame) { + canSaveDynamicFields = "0"; + Profile = "GuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "250 235"; + Extent = "300 102"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = ""; + closeCommand="MessageCallback(MessageBoxYesNoCancelDlg,MessageBoxYesNoCancelDlg.cancelCallback);"; + + new GuiMLTextCtrl(MBYesNoCancelText) { + canSaveDynamicFields = "0"; + Profile = "GuiMLTextProfile"; + HorizSizing = "center"; + VertSizing = "bottom"; + position = "7 38"; + Extent = "286 14"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "7 71"; + Extent = "80 22"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MessageCallback(MessageBoxYesNoCancelDlg,MessageBoxYesNoCancelDlg.yesCallback);"; + Accelerator = "return"; + hovertime = "1000"; + text = "Yes"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "92 71"; + Extent = "80 22"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MessageCallback(MessageBoxYesNoCancelDlg,MessageBoxYesNoCancelDlg.noCallback);"; + hovertime = "1000"; + text = "No"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "213 71"; + Extent = "80 22"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "MessageCallback(MessageBoxYesNoCancelDlg,MessageBoxYesNoCancelDlg.cancelCallback);"; + Accelerator = "escape"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/messageBoxes/messagePopup.ed.gui b/Templates/BaseGame/game/tools/gui/messageBoxes/messagePopup.ed.gui new file mode 100644 index 000000000..806f12204 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/messageBoxes/messagePopup.ed.gui @@ -0,0 +1,46 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessagePopupDlg) { + profile = "GuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl(MessagePopFrame) { + profile = "GuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "170 175"; + extent = "300 92"; + minExtent = "48 92"; + visible = "1"; + helpTag = "0"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + text = ""; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + + new GuiMLTextCtrl(MessagePopText) { + profile = "GuiMLTextProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "32 39"; + extent = "236 24"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/objectSelection.ed.cs b/Templates/BaseGame/game/tools/gui/objectSelection.ed.cs new file mode 100644 index 000000000..bd2fa67d9 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/objectSelection.ed.cs @@ -0,0 +1,368 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Common code for object selection dialogs. + + +//============================================================================================= +// Initialization. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::init( %this ) +{ + // Initialize the class list. + + %classList = %this-->classList; + if( isObject( %classList ) ) + %this.initClassList(); + + // Initialize the filter list. + + %filterList = %this-->filterList; + if( isObject( %filterList ) ) + %this.initFilterList(); + + // Initialize the group list. + + %groupList = %this-->groupList; + if( isObject( %groupList ) ) + %this.initGroupList(); +} + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::cleanup( %this ) +{ + // Clear the class list. + + %classList = %this-->classList; + if( isObject( %classList ) ) + %classList.clear(); + + // Clear the filter list. + + %filterList = %this-->filterList; + if( isObject( %filterList ) ) + %filterList.clear(); + + // Clear the group list. + + %groupList = %this-->groupList; + if( isObject( %groupList ) ) + %groupList.clear(); + + // Delete the class array. + + if( isObject( %this.classArray ) ) + %this.classArray.delete(); +} + +//============================================================================================= +// Methods to override in a subclass. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +/// Return the group object where onSelectObjects should begin searching for objects. +function EObjectSelection::getRootGroup( %this ) +{ + return RootGroup; +} + +//--------------------------------------------------------------------------------------------- + +/// Return a set that contains all filter objects to include in the filter list. +/// Returning 0 will leave the filter list empty. +function EObjectSelection::getFilterSet( %this ) +{ + return 0; +} + +//--------------------------------------------------------------------------------------------- + +/// Return true if the given class should be included in the class list. +function EObjectSelection::includeClass( %this, %className ) +{ + return true; +} + +//--------------------------------------------------------------------------------------------- + +/// The object has met the given criteria. Select or deselect it depending on %val. +function EObjectSelection::selectObject( %this, %object, %val ) +{ +} + +//============================================================================================= +// Events. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::onSelectObjects( %this, %val ) +{ + // Get the root group to search in. + + %groupList = %this-->groupList; + if( !isObject( %groupList ) ) + %root = %this.getRootGroup(); + else + %root = %groupList.getSelected(); + + if( !isObject( %root ) ) + return; + + // Fetch the object name pattern. + + %namePatternField = %this-->namePattern; + if( isObject( %namePatternField ) ) + %this.namePattern = %namePatternField.getText(); + else + %this.namePattern = ""; + + // Clear current selection first, if need be. + + if( %val ) + { + %retainSelectionBox = %this-->retainSelection; + if( isObject( %retainSelectionBox ) && !%retainSelectionBox.isStateOn() ) + %this.clearSelection(); + } + + // (De)Select all matching objects in it. + + %this.selectObjectsIn( %root, %val, true ); +} + +//============================================================================================= +// Selection. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::selectObjectsIn( %this, %group, %val, %excludeGroup ) +{ + // Match to the group itself. + + if( !%excludeGroup && %this.objectMatchesCriteria( %group ) ) + %this.selectObject( %group, %val ); + + // Recursively match all children. + + foreach( %obj in %group ) + { + if( %obj.isMemberOfClass( "SimSet" ) ) + %this.selectObjectsIn( %obj, %val ); + else if( %this.objectMatchesCriteria( %obj ) ) + %this.selectObject( %obj, %val ); + } +} + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::objectMatchesCriteria( %this, %object ) +{ + // Check name. + + if( %this.namePattern !$= "" && !strIsMatchExpr( %this.namePattern, %object.getName() ) ) + return false; + + // Check class. + + if( !%this.isClassEnabled( %object.getClassName() ) ) + return false; + + return true; +} + +//============================================================================================= +// Groups. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::initGroupList( %this ) +{ + %groupList = %this-->groupList; + + %selected = 0; + if( %groupList.size() > 0 ) + %selected = %groupList.getSelected(); + + %groupList.clear(); + + %root = %this.getRootGroup(); + if( !isObject( %root ) ) + return; + + // Add all non-empty groups. + + %this.scanGroup( %root, %groupList, 0 ); + + // Select initial group. + + if( %selected != 0 && isObject( %selected ) ) + %groupList.setSelected( %selected ); + else + %groupList.setSelected( %root.getId() ); +} + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::scanGroup( %this, %group, %list, %indentLevel ) +{ + // Create a display name for the group. + + %text = %group.getName(); + if( %text $= "" ) + %text = %group.getClassName(); + + %internalName = %group.getInternalName(); + if( %internalName !$= "" ) + %text = %text @ " [" @ %internalName @ "]"; + + // Indent the name according to the depth in the hierarchy. + + if( %indentLevel > 0 ) + %text = strrepeat( " ", %indentLevel ) @ %text; + + // Add it to the list. + + %list.add( %text, %group.getId() ); + + // Recurse into SimSets with at least one child. + + foreach ( %obj in %group ) + { + if( !%obj.isMemberOfClass( "SimSet" ) + || %obj.getCount() == 0 ) + continue; + + %this.scanGroup( %obj, %list, %indentLevel + 1 ); + } +} + +//============================================================================================= +// Filters. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::initFilterList( %this ) +{ + %filterList = %this-->filterList; +} + +//============================================================================================= +// Classes. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +/// Initialize the list of class toggles. +function EObjectSelection::initClassList( %this ) +{ + %classArray = new ArrayObject(); + %this.classArray = %classArray; + + // Add all classes to the array. + + %classes = enumerateConsoleClasses(); + foreach$( %className in %classes ) + { + if( !%this.includeClass( %className ) ) + continue; + + %classArray.push_back( %className, true ); + } + + // Sort the class list. + + %classArray.sortk( true ); + + // Add checkboxes for all classes to the list. + + %classList = %this-->classList; + %count = %classArray.count(); + for( %i = 0; %i < %count; %i ++ ) + { + %className = %classArray.getKey( %i ); + %textLength = strlen( %className ); + %text = " " @ %className; + + %checkBox = new GuiCheckBoxCtrl() + { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxListFlipedProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = ( %textLength * 4 ) @ " 18"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Include/exclude all " @ %className @ " objects."; + text = %text; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + command = %classArray @ ".setValue( $ThisControl.getValue(), " @ %i @ " );"; + }; + + %checkBox.setStateOn( true ); + %classList.addGuiControl( %checkBox ); + } +} + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::selectAllInClassList( %this, %state ) +{ + %classList = %this-->classList; + + foreach( %ctrl in %classList ) + { + if( %ctrl.getValue() == %state ) + %ctrl.performClick(); + } +} + +//--------------------------------------------------------------------------------------------- + +function EObjectSelection::isClassEnabled( %this, %className ) +{ + // Look up the class entry in the array. + + %index = %this.classArray.getIndexFromKey( %className ); + if( %index == -1 ) + return false; + + // Return the flag. + + return %this.classArray.getValue( %index ); +} diff --git a/Templates/BaseGame/game/tools/gui/openFileDialog.ed.cs b/Templates/BaseGame/game/tools/gui/openFileDialog.ed.cs new file mode 100644 index 000000000..50c7cdfcc --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/openFileDialog.ed.cs @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function getLoadFilename(%filespec, %callback, %currentFile, %getRelative, %defaultPath) +{ + //If no default path passed in then try to get one from the file + if(%defaultPath $= "") + { + if ( filePath( %currentFile ) !$= "" ) + %defaultPath = filePath(%currentFile); + } + + %dlg = new OpenFileDialog() + { + Filters = %filespec; + DefaultFile = %currentFile; + DefaultPath = %defaultPath; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + %ok = %dlg.Execute(); + if ( %ok ) + { + %file = %dlg.FileName; + if(%getRelative) + %file = strreplace(%file,getWorkingDirectory() @ "/", ""); + eval(%callback @ "(\"" @ %file @ "\");"); + $Tools::FileDialogs::LastFilePath = filePath( %dlg.FileName ); + } + + %dlg.delete(); + + return %ok; +} + +// Opens a choose file dialog with format filters already loaded +// in. This avoids the issue of passing a massive list of format +// filters into a function as an arguement. +function getLoadFormatFilename(%callback, %currentFile) +{ + %dlg = new OpenFileDialog() + { + Filters = getFormatFilters() @ "(All Files (*.*)|*.*|"; + DefaultFile = %currentFile; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + if ( filePath( %currentFile ) !$= "" ) + %dlg.DefaultPath = filePath(%currentFile); + + if ( %dlg.Execute() ) + { + eval(%callback @ "(\"" @ %dlg.FileName @ "\");"); + $Tools::FileDialogs::LastFilePath = filePath( %dlg.FileName ); + } + + %dlg.delete(); +} diff --git a/Templates/BaseGame/game/tools/gui/profilerGraph.cs b/Templates/BaseGame/game/tools/gui/profilerGraph.cs new file mode 100644 index 000000000..07cc2b940 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/profilerGraph.cs @@ -0,0 +1,113 @@ +$ProfilerGraph::refreshRate = 32; +// Profiles +new GuiControlProfile (ProfilerGraphProfile) +{ + modal = false; + opaque = false; + canKeyFocus = false; +}; + +new GuiControlProfile (ProfilerGraphKeyContainerProfile) +{ + border = true; + opaque = true; + fillColor = "100 100 100 200"; +}; +new GuiControlProfile (ProfilerGraphGraphFrameRateProfile) +{ + border = false; + fontColor = "255 255 255"; +}; +new GuiControlProfile (ProfilerGraphPolyCountProfile) +{ + border = false; + fontColor = "255 0 0"; +}; +new GuiControlProfile (ProfilerGraphDrawCountProfile) +{ + border = false; + fontColor = "0 255 0"; +}; +new GuiControlProfile (ProfilerGraphRTChangesProfile) +{ + border = false; + fontColor = "0 0 255"; +}; +new GuiControlProfile (ProfilerGraphLatencyProfile) +{ + border = false; + fontColor = "0 255 255"; +}; +new GuiControlProfile (ProfilerGraphPacketLossProfile) +{ + border = false; + fontColor = "0 0 0"; +}; + +function toggleProfilerGraph() +{ + if(!$ProfilerGraph::isInitialized) + { + ProfilerGraph::updateStats(); + $ProfilerGraph::isInitialized = true; + } + + if(!Canvas.isMember(ProfilerGraphGui)) + { + Canvas.add(ProfilerGraphGui); + } + else + Canvas.remove(ProfilerGraphGui); +} + +function ProfilerGraph::updateStats() +{ + $ProfilerGraphThread = ProfilerGraph.schedule($ProfilerGraph::refreshRate, "updateStats"); + + if(!$Stats::netGhostUpdates) + return; + + if(isobject(ProfilerGraph)) + { + GhostsActive.setText("Frame Rate: " @ $fps::real); + ProfilerGraph.addDatum(1,$fps::real); + + GhostUpdates.setText("Poly Count: " @ $GFXDeviceStatistics::polyCount); + ProfilerGraph.addDatum(2,$GFXDeviceStatistics::polyCount); + + BitsSent.setText("Draw Calls: " @ $GFXDeviceStatistics::drawCalls); + ProfilerGraph.addDatum(3,$GFXDeviceStatistics::drawCalls); + + BitsReceived.setText("Render Target Changes: " @ $GFXDeviceStatistics::renderTargetChanges); + ProfilerGraph.addDatum(4,$GFXDeviceStatistics::renderTargetChanges); + + ProfilerGraph.matchScale(2,3); + + //Latency.setText("Latency: " @ ServerConnection.getPing()); + //ProfilerGraph.addDatum(5,ServerConnection.getPacketLoss()); + + //PacketLoss.setText("Packet Loss: " @ ServerConnection.getPacketLoss()); + } +} + +function ProfilerGraph::toggleKey() +{ + if(!GhostsActive.visible) + { + GhostsActive.visible = 1; + GhostUpdates.visible = 1; + BitsSent.visible = 1; + BitsReceived.visible = 1; + Latency.visible = 1; + PacketLoss.visible = 1; + } + else + { + GhostsActive.visible = 0; + GhostUpdates.visible = 0; + BitsSent.visible = 0; + BitsReceived.visible = 0; + Latency.visible = 0; + PacketLoss.visible = 0; + } +} diff --git a/Templates/BaseGame/game/tools/gui/profilerGraph.gui b/Templates/BaseGame/game/tools/gui/profilerGraph.gui new file mode 100644 index 000000000..a008c227a --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/profilerGraph.gui @@ -0,0 +1,370 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ProfilerGraphGui) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ProfilerGraphProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + noCursor = "1"; + + new GuiWindowCtrl() { + text = "Profiler"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "1"; + canMaximize = "1"; + canCollapse = "0"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "96 232"; + extent = "558 283"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabBookCtrl() { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "64"; + tabHeight = "20"; + allowReorder = "0"; + defaultPage = "-1"; + selectedPage = "0"; + frontTabPadding = "0"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 22"; + extent = "554 261"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabBookProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl() { + fitBook = "1"; + text = "Main Render"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 20"; + extent = "554 241"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "6 13"; + extent = "103 223"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ProfilerGraphKeyContainerProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + hidden = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl(GhostsActive) { + text = "Ghosts Active"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 0"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(GhostUpdates) { + text = "Ghost Updates"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "3 72"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(BitsSent) { + text = "Bytes Sent"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 18"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(BitsReceived) { + text = "Bytes Received"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "3 90"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(Latency) { + text = "Latency"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 36"; + extent = "100 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ProfilerGraphLatencyProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl(PacketLoss) { + text = "Packet Loss"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "3 108"; + extent = "59 18"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ProfilerGraphPacketLossProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiGraphCtrl(ProfilerGraph) { + centerY = "1"; + plotColor[0] = "1 1 1 1"; + plotColor[1] = "1 0 0 1"; + plotColor[2] = "0 1 0 1"; + plotColor[3] = "0 0 1 1"; + plotColor[4] = "0 1 1 1"; + plotColor[5] = "0 0 0 1"; + plotType[0] = "PolyLine"; + plotType[1] = "PolyLine"; + plotType[2] = "PolyLine"; + plotType[3] = "PolyLine"; + plotType[4] = "PolyLine"; + plotType[5] = "PolyLine"; + plotInterval[0] = "0"; + plotInterval[1] = "0"; + plotInterval[2] = "0"; + plotInterval[3] = "0"; + plotInterval[4] = "0"; + plotInterval[5] = "0"; + position = "112 5"; + extent = "440 231"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ProfilerGraphKeyContainerProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Frame Rate"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "9 0"; + extent = "99 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Poly Count"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "9 32"; + extent = "99 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Draw Count"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "9 64"; + extent = "99 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "RT Changes"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "9 96"; + extent = "99 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/gui/profiles.ed.cs b/Templates/BaseGame/game/tools/gui/profiles.ed.cs new file mode 100644 index 000000000..04e8df5b2 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/profiles.ed.cs @@ -0,0 +1,1079 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function execEditorProfilesCS() +{ + exec("./profiles.ed.cs"); +} + +$Gui::clipboardFile = expandFilename("./clipboard.gui"); + + +if( !isObject( ToolsGuiDefaultProfile ) ) +new GuiControlProfile (ToolsGuiDefaultProfile) +{ + tab = false; + canKeyFocus = false; + hasBitmapArray = false; + mouseOverSelected = false; + + // fill color + opaque = false; + fillColor = "242 241 240"; + fillColorHL ="228 228 235"; + fillColorSEL = "98 100 137"; + fillColorNA = "255 255 255 "; + + // border color + border = 0; + borderColor = "100 100 100"; + borderColorHL = "50 50 50 50"; + borderColorNA = "75 75 75"; + + // font + fontType = "Arial"; + fontSize = 14; + fontCharset = ANSI; + + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorNA = "0 0 0"; + fontColorSEL= "255 255 255"; + + // bitmap information + bitmap = ""; + bitmapBase = ""; + textOffset = "0 0"; + + // used by guiTextControl + modal = true; + justify = "left"; + autoSizeWidth = false; + autoSizeHeight = false; + returnTab = false; + numbersOnly = false; + cursorColor = "0 0 0 255"; + + // sounds + //soundButtonDown = ""; + //soundButtonOver = ""; +}; + +if( !isObject( ToolsGuiSolidDefaultProfile ) ) +new GuiControlProfile (ToolsGuiSolidDefaultProfile) +{ + opaque = true; + border = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTransparentProfile ) ) +new GuiControlProfile (ToolsGuiTransparentProfile) +{ + opaque = false; + border = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiGroupBorderProfile ) ) +new GuiControlProfile( ToolsGuiGroupBorderProfile ) +{ + border = false; + opaque = false; + hasBitmapArray = true; + bitmap = "./images/group-border"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTabBorderProfile ) ) +new GuiControlProfile( ToolsGuiTabBorderProfile ) +{ + border = false; + opaque = false; + hasBitmapArray = true; + bitmap = "./images/tab-border"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiToolTipProfile ) ) +new GuiControlProfile (ToolsGuiToolTipProfile) +{ + // fill color + fillColor = "239 237 222"; + + // border color + borderColor = "138 134 122"; + + // font + fontType = "Arial"; + fontSize = 14; + fontColor = "0 0 0"; + + category = "Tools"; +}; + +if( !isObject( ToolsGuiModelessDialogProfile ) ) +new GuiControlProfile( ToolsGuiModelessDialogProfile ) +{ + modal = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiFrameSetProfile ) ) +new GuiControlProfile (ToolsGuiFrameSetProfile) +{ + fillcolor = "255 255 255"; + borderColor = "246 245 244"; + border = 1; + opaque = true; + border = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiWindowProfile ) ) +new GuiControlProfile (ToolsGuiWindowProfile) +{ + opaque = false; + border = 2; + fillColor = "242 241 240"; + fillColorHL = "221 221 221"; + fillColorNA = "200 200 200"; + fontColor = "50 50 50"; + fontColorHL = "0 0 0"; + bevelColorHL = "255 255 255"; + bevelColorLL = "0 0 0"; + text = "untitled"; + bitmap = "./images/window"; + textOffset = "8 4"; + hasBitmapArray = true; + justify = "left"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiToolbarWindowProfile ) ) +new GuiControlProfile(ToolsGuiToolbarWindowProfile : ToolsGuiWindowProfile) +{ + bitmap = "./images/toolbar-window"; + text = ""; + category = "Tools"; +}; + +if( !isObject( ToolsGuiWindowCollapseProfile ) ) +new GuiControlProfile (ToolsGuiWindowCollapseProfile : ToolsGuiWindowProfile) +{ + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextProfile ) ) +new GuiControlProfile (ToolsGuiTextProfile) +{ + justify = "left"; + fontColor = "20 20 20"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextBoldCenterProfile ) ) +new GuiControlProfile (ToolsGuiTextBoldCenterProfile : ToolsGuiTextProfile) +{ + fontColor = "50 50 50"; + fontType = "Arial Bold"; + fontSize = 16; + justify = "center"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextRightProfile ) ) +new GuiControlProfile (ToolsGuiTextRightProfile : ToolsGuiTextProfile) +{ + justify = "right"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextCenterProfile ) ) +new GuiControlProfile (ToolsGuiTextCenterProfile : ToolsGuiTextProfile) +{ + justify = "center"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiInspectorTitleTextProfile ) ) +new GuiControlProfile (ToolsGuiInspectorTitleTextProfile) +{ + fontColor = "100 100 100"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiAutoSizeTextProfile ) ) +new GuiControlProfile (ToolsGuiAutoSizeTextProfile) +{ + fontColor = "0 0 0"; + autoSizeWidth = true; + autoSizeHeight = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiMLTextProfile ) ) +new GuiControlProfile( ToolsGuiMLTextProfile ) +{ + fontColorLink = "100 100 100"; + fontColorLinkHL = "255 255 255"; + autoSizeWidth = true; + autoSizeHeight = true; + border = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextArrayProfile ) ) +new GuiControlProfile( ToolsGuiTextArrayProfile : ToolsGuiTextProfile ) +{ + fontColor = "50 50 50"; + fontColorHL = " 0 0 0"; + fontColorSEL = "0 0 0"; + fillColor ="200 200 200"; + fillColorHL = "228 228 235"; + fillColorSEL = "200 200 200"; + border = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextListProfile ) ) +new GuiControlProfile( ToolsGuiTextListProfile : ToolsGuiTextProfile ) +{ + tab = true; + canKeyFocus = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextEditProfile ) ) +new GuiControlProfile( ToolsGuiTextEditProfile ) +{ + opaque = true; + bitmap = "./images/textEditFrame"; + hasBitmapArray = true; + border = -2; // fix to display textEdit img + //borderWidth = "1"; // fix to display textEdit img + //borderColor = "100 100 100"; + fillColor = "242 241 240 0"; + fillColorHL = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fontColorSEL = "98 100 137"; + fontColorNA = "200 200 200"; + textOffset = "4 2"; + autoSizeWidth = false; + autoSizeHeight = true; + justify = "left"; + tab = true; + canKeyFocus = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiNumericTextEditProfile ) ) +new GuiControlProfile( ToolsGuiNumericTextEditProfile : ToolsGuiTextEditProfile ) +{ + numbersOnly = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiNumericDropSliderTextProfile ) ) +new GuiControlProfile( ToolsGuiNumericDropSliderTextProfile : ToolsGuiTextEditProfile ) +{ + bitmap = "./images/textEditSliderBox"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiRLProgressBitmapProfile ) ) +new GuiControlProfile( ToolsGuiRLProgressBitmapProfile ) +{ + border = false; + hasBitmapArray = true; + bitmap = "./images/rl-loadingbar"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiProgressTextProfile ) ) +new GuiControlProfile( ToolsGuiProgressTextProfile ) +{ + fontSize = "14"; + fontType = "Arial"; + fontColor = "0 0 0"; + justify = "center"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiButtonProfile ) ) +new GuiControlProfile( ToolsGuiButtonProfile ) +{ + opaque = true; + border = true; + fontColor = "50 50 50"; + fontColorHL = "0 0 0"; + fontColorNA = "200 200 200"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "./images/button"; + hasBitmapArray = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiThumbHighlightButtonProfile ) ) +new GuiControlProfile( ToolsGuiThumbHighlightButtonProfile : ToolsGuiButtonProfile ) +{ + bitmap = "./images/thumbHightlightButton"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiIconButtonProfile ) ) +new GuiControlProfile( ToolsGuiIconButtonProfile ) +{ + opaque = true; + border = true; + fontColor = "50 50 50"; + fontColorHL = "0 0 0"; + fontColorNA = "200 200 200"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "./images/iconbutton"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiIconButtonSmallProfile ) ) +new GuiControlProfile( ToolsGuiIconButtonSmallProfile : ToolsGuiIconButtonProfile ) +{ + bitmap = "./images/iconbuttonsmall"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiEditorTabPage ) ) +new GuiControlProfile(ToolsGuiEditorTabPage) +{ + opaque = true; + border = false; + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "./images/tab"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiCheckBoxProfile ) ) +new GuiControlProfile( ToolsGuiCheckBoxProfile ) +{ + opaque = false; + fillColor = "232 232 232"; + border = false; + borderColor = "100 100 100"; + fontSize = 14; + fontColor = "20 20 20"; + fontColorHL = "80 80 80"; + fontColorNA = "200 200 200"; + fixedExtent = true; + justify = "left"; + bitmap = "./images/checkbox"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiCheckBoxListProfile ) ) +new GuiControlProfile( ToolsGuiCheckBoxListProfile : ToolsGuiCheckBoxProfile) +{ + bitmap = "./images/checkbox-list"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiCheckBoxListFlipedProfile ) ) +new GuiControlProfile( ToolsGuiCheckBoxListFlipedProfile : ToolsGuiCheckBoxProfile) +{ + bitmap = "./images/checkbox-list_fliped"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiInspectorCheckBoxTitleProfile ) ) +new GuiControlProfile( ToolsGuiInspectorCheckBoxTitleProfile : ToolsGuiCheckBoxProfile ){ + fontColor = "100 100 100"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiRadioProfile ) ) +new GuiControlProfile( ToolsGuiRadioProfile ) +{ + fontSize = 14; + fillColor = "232 232 232"; + fontColor = "20 20 20"; + fontColorHL = "80 80 80"; + fixedExtent = true; + bitmap = "./images/radioButton"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiScrollProfile ) ) +new GuiControlProfile( ToolsGuiScrollProfile ) +{ + opaque = true; + fillcolor = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "150 150 150"; + border = true; + bitmap = "./images/scrollBar"; + hasBitmapArray = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiOverlayProfile ) ) +new GuiControlProfile( ToolsGuiOverlayProfile ) +{ + opaque = true; + fillcolor = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fillColor = "0 0 0 100"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiSliderProfile ) ) +new GuiControlProfile( ToolsGuiSliderProfile ) +{ + bitmap = "./images/slider"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiSliderBoxProfile ) ) +new GuiControlProfile( ToolsGuiSliderBoxProfile ) +{ + bitmap = "./images/slider-w-box"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiPopupMenuItemBorder ) ) +new GuiControlProfile( ToolsGuiPopupMenuItemBorder : ToolsGuiButtonProfile ) +{ + opaque = true; + border = true; + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorNA = "255 255 255"; + fixedExtent = false; + justify = "center"; + canKeyFocus = false; + bitmap = "./images/button"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiPopUpMenuDefault ) ) +new GuiControlProfile( ToolsGuiPopUpMenuDefault : ToolsGuiDefaultProfile ) +{ + opaque = true; + mouseOverSelected = true; + textOffset = "3 3"; + border = 0; + borderThickness = 0; + fixedExtent = true; + bitmap = "./images/scrollbar"; + hasBitmapArray = true; + profileForChildren = ToolsGuiPopupMenuItemBorder; + fillColor = "242 241 240 ";//"255 255 255";//100 + fillColorHL = "228 228 235 ";//"204 203 202"; + fillColorSEL = "98 100 137 ";//"204 203 202"; + // font color is black + fontColorHL = "0 0 0 ";//"0 0 0"; + fontColorSEL = "255 255 255";//"0 0 0"; + borderColor = "100 100 100"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiPopUpMenuProfile ) ) +new GuiControlProfile( ToolsGuiPopUpMenuProfile : ToolsGuiPopUpMenuDefault ) +{ + textOffset = "6 4"; + bitmap = "./images/dropDown"; + hasBitmapArray = true; + border = 1; + profileForChildren = ToolsGuiPopUpMenuDefault; + category = "Tools"; +}; + +if( !isObject( ToolsGuiPopUpMenuTabProfile ) ) +new GuiControlProfile( ToolsGuiPopUpMenuTabProfile : ToolsGuiPopUpMenuDefault ) +{ + bitmap = "./images/dropDown-tab"; + textOffset = "6 4"; + canKeyFocus = true; + hasBitmapArray = true; + border = 1; + profileForChildren = ToolsGuiPopUpMenuDefault; + category = "Tools"; +}; + +if( !isObject( ToolsGuiPopUpMenuEditProfile ) ) +new GuiControlProfile( ToolsGuiPopUpMenuEditProfile : ToolsGuiPopUpMenuDefault ) +{ + textOffset = "6 4"; + canKeyFocus = true; + bitmap = "./images/dropDown"; + hasBitmapArray = true; + border = 1; + profileForChildren = ToolsGuiPopUpMenuDefault; + category = "Tools"; +}; + +if( !isObject( ToolsGuiListBoxProfile ) ) +new GuiControlProfile( ToolsGuiListBoxProfile ) +{ + tab = true; + canKeyFocus = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTabBookProfile ) ) +new GuiControlProfile( ToolsGuiTabBookProfile ) +{ + fillColorHL = "100 100 100"; + fillColorNA = "150 150 150"; + fontColor = "30 30 30"; + fontColorHL = "0 0 0"; + fontColorNA = "50 50 50"; + fontType = "Arial"; + fontSize = 14; + justify = "center"; + bitmap = "./images/tab"; + tabWidth = 64; + tabHeight = 24; + tabPosition = "Top"; + tabRotation = "Horizontal"; + textOffset = "0 -3"; + tab = true; + cankeyfocus = true; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTabBookNoBitmapProfile ) ) +new GuiControlProfile( ToolsGuiTabBookNoBitmapProfile : ToolsGuiTabBookProfile ) +{ + bitmap = ""; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTabPageProfile ) ) +new GuiControlProfile( ToolsGuiTabPageProfile : ToolsGuiDefaultProfile ) +{ + fontType = "Arial"; + fontSize = 10; + justify = "center"; + bitmap = "./images/tab"; + opaque = false; + fillColor = "240 239 238"; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTreeViewProfile ) ) +new GuiControlProfile( ToolsGuiTreeViewProfile ) +{ + bitmap = "./images/treeView"; + autoSizeHeight = true; + canKeyFocus = true; + fillColor = "255 255 255"; + fillColorHL = "228 228 235"; + fillColorSEL = "98 100 137"; + fillColorNA = "255 255 255"; + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorSEL= "255 255 255"; + fontColorNA = "200 200 200"; + borderColor = "128 000 000"; + borderColorHL = "255 228 235"; + fontSize = 14; + opaque = false; + border = false; + category = "Tools"; +}; + +if( !isObject( ToolsGuiTextPadProfile ) ) +new GuiControlProfile( ToolsGuiTextPadProfile ) +{ + fontType = ($platform $= "macos") ? "Monaco" : "Lucida Console"; + fontSize = ($platform $= "macos") ? 13 : 12; + tab = true; + canKeyFocus = true; + + // Deviate from the Default + opaque=true; + fillColor = "255 255 255"; + border = 0; + category = "Tools"; +}; + +if( !isObject( ToolsGuiFormProfile ) ) +new GuiControlProfile( ToolsGuiFormProfile : ToolsGuiTextProfile ) +{ + opaque = false; + border = 5; + justify = "center"; + profileForChildren = ToolsGuiButtonProfile; + opaque = false; + hasBitmapArray = true; + bitmap = "./images/button"; + category = "Tools"; +}; + +// ---------------------------------------------------------------------------- + +singleton GuiControlProfile( GuiEditorClassProfile ) +{ + opaque = true; + fillColor = "232 232 232"; + border = 1; + borderColor = "40 40 40 140"; + borderColorHL = "127 127 127"; + fontColor = "0 0 0"; + fontColorHL = "50 50 50"; + fixedExtent = true; + justify = "center"; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiBackFillProfile ) +{ + opaque = true; + fillColor = "0 94 94"; + border = true; + borderColor = "255 128 128"; + fontType = "Arial"; + fontSize = 12; + fontColor = "0 0 0"; + fontColorHL = "50 50 50"; + fixedExtent = true; + justify = "center"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiControlListPopupProfile ) +{ + opaque = true; + fillColor = "255 255 255"; + fillColorHL = "204 203 202"; + border = false; + //borderColor = "0 0 0"; + fontColor = "0 0 0"; + fontColorHL = "0 0 0"; + fontColorNA = "50 50 50"; + textOffset = "0 2"; + autoSizeWidth = false; + autoSizeHeight = true; + tab = true; + canKeyFocus = true; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiSceneGraphEditProfile ) +{ + canKeyFocus = true; + tab = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorButtonProfile : ToolsGuiButtonProfile ) +{ + //border = 1; + justify = "Center"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorSwatchButtonProfile ) +{ + borderColor = "100 100 100 255"; + borderColorNA = "200 200 200 255"; + fillColorNA = "255 255 255 0"; + borderColorHL = "0 0 0 255"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorTextEditProfile ) +{ + // Transparent Background + opaque = true; + fillColor = "0 0 0 0"; + fillColorHL = "255 255 255"; + + // No Border (Rendered by field control) + border = false; + + tab = true; + canKeyFocus = true; + + // font + fontType = "Arial"; + fontSize = 14; + + fontColor = "0 0 0"; + fontColorSEL = "43 107 206"; + fontColorHL = "244 244 244"; + fontColorNA = "100 100 100"; + category = "Editor"; +}; +singleton GuiControlProfile( GuiDropdownTextEditProfile : ToolsGuiTextEditProfile ) +{ + bitmap = "tools/gui/images/dropdown-textEdit"; + category = "Editor"; +}; +singleton GuiControlProfile( GuiInspectorTextEditRightProfile : GuiInspectorTextEditProfile ) +{ + justify = "right"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorGroupProfile ) +{ + fontType = "Arial"; + fontSize = "14"; + + fontColor = "0 0 0 150"; + fontColorHL = "25 25 25 220"; + fontColorNA = "128 128 128"; + + justify = "left"; + opaque = false; + border = false; + + bitmap = "tools/editorClasses/gui/images/rollout"; + + textOffset = "20 0"; + + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorFieldProfile) +{ + // fill color + opaque = false; + fillColor = "255 255 255"; + fillColorHL = "204 203 202"; + fillColorNA = "244 244 244"; + + // border color + border = false; + borderColor = "190 190 190"; + borderColorHL = "156 156 156"; + borderColorNA = "200 200 200"; + + //bevelColorHL = "255 255 255"; + //bevelColorLL = "0 0 0"; + + // font + fontType = "Arial"; + fontSize = 14; + + fontColor = "32 32 32"; + fontColorHL = "50 50 50"; + fontColorNA = "190 190 190"; + textOffset = "10 0"; + + tab = true; + canKeyFocus = true; + category = "Editor"; +}; + +/* +singleton GuiControlProfile( GuiInspectorMultiFieldProfile : GuiInspectorFieldProfile ) +{ + opaque = true; + fillColor = "50 50 230 30"; +}; +*/ + +singleton GuiControlProfile( GuiInspectorMultiFieldDifferentProfile : GuiInspectorFieldProfile ) +{ + border = true; + borderColor = "190 100 100"; +}; + +singleton GuiControlProfile( GuiInspectorDynamicFieldProfile : GuiInspectorFieldProfile ) +{ + // Transparent Background + opaque = true; + fillColor = "0 0 0 0"; + fillColorHL = "255 255 255"; + + // No Border (Rendered by field control) + border = false; + + tab = true; + canKeyFocus = true; + + // font + fontType = "Arial"; + fontSize = 14; + + fontColor = "0 0 0"; + fontColorSEL = "43 107 206"; + fontColorHL = "244 244 244"; + fontColorNA = "100 100 100"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiRolloutProfile ) +{ + border = 1; + borderColor = "200 200 200"; + + hasBitmapArray = true; + bitmap = "tools/editorClasses/gui/images/rollout"; + + textoffset = "17 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorRolloutProfile0 ) +{ + // font + fontType = "Arial"; + fontSize = 14; + + fontColor = "32 32 32"; + fontColorHL = "32 100 100"; + fontColorNA = "0 0 0"; + + justify = "left"; + opaque = false; + + border = 0; + borderColor = "190 190 190"; + borderColorHL = "156 156 156"; + borderColorNA = "64 64 64"; + + bitmap = "tools/editorclasses/gui/images/rollout_plusminus_header"; + + textOffset = "20 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorStackProfile ) +{ + opaque = false; + border = false; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorProfile : GuiInspectorFieldProfile ) +{ + opaque = true; + fillColor = "255 255 255 255"; + border = 0; + cankeyfocus = true; + tab = true; + category = "Editor"; +}; +singleton GuiControlProfile( GuiInspectorInfoProfile : GuiInspectorFieldProfile ) +{ + opaque = true; + fillColor = "242 241 240"; + border = 0; + cankeyfocus = true; + tab = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorBackgroundProfile : GuiInspectorFieldProfile ) +{ + border = 0; + cankeyfocus=true; + tab = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorTypeFileNameProfile ) +{ + // Transparent Background + opaque = false; + + // No Border (Rendered by field control) + border = 0; + + tab = true; + canKeyFocus = true; + + // font + fontType = "Arial"; + fontSize = 14; + + // Center text + justify = "center"; + + fontColor = "32 32 32"; + fontColorHL = "50 50 50"; + fontColorNA = "0 0 0"; + + fillColor = "255 255 255"; + fillColorHL = "204 203 202"; + fillColorNA = "244 244 244"; + + borderColor = "190 190 190"; + borderColorHL = "156 156 156"; + borderColorNA = "64 64 64"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorColumnCtrlProfile : GuiInspectorFieldProfile ) +{ + opaque = true; + fillColor = "210 210 210"; + border = 0; + category = "Editor"; +}; + +singleton GuiControlProfile( InspectorTypeEnumProfile : GuiInspectorFieldProfile ) +{ + mouseOverSelected = true; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + opaque=true; + border=true; + textOffset = "4 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( InspectorTypeCheckboxProfile : GuiInspectorFieldProfile ) +{ + bitmap = "tools/gui/images/checkBox"; + hasBitmapArray = true; + opaque=false; + border=false; + textOffset = "4 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiToolboxButtonProfile : ToolsGuiButtonProfile ) +{ + justify = "center"; + fontColor = "0 0 0"; + border = 0; + textOffset = "0 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiDirectoryTreeProfile : ToolsGuiTreeViewProfile ) +{ + fontColor = "40 40 40"; + fontColorSEL= "250 250 250 175"; + fillColorHL = "0 60 150"; + fontColorNA = "240 240 240"; + fontType = "Arial"; + fontSize = 14; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiDirectoryFileListProfile ) +{ + fontColor = "40 40 40"; + fontColorSEL= "250 250 250 175"; + fillColorHL = "0 60 150"; + fontColorNA = "240 240 240"; + fontType = "Arial"; + fontSize = 14; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiDragAndDropProfile ) +{ + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorFieldInfoPaneProfile ) +{ + opaque = false; + fillcolor = GuiInspectorBackgroundProfile.fillColor; + borderColor = ToolsGuiDefaultProfile.borderColor; + border = 1; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiInspectorFieldInfoMLTextProfile : ToolsGuiMLTextProfile ) +{ + opaque = false; + border = 0; + textOffset = "5 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiEditorScrollProfile ) +{ + opaque = true; + fillcolor = GuiInspectorBackgroundProfile.fillColor; + borderColor = ToolsGuiDefaultProfile.borderColor; + border = 1; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiCreatorIconButtonProfile ) +{ + opaque = true; + fillColor = "225 243 252 255"; + fillColorHL = "225 243 252 0"; + fillColorNA = "225 243 252 0"; + fillColorSEL = "225 243 252 0"; + + //tab = true; + //canKeyFocus = true; + + fontType = "Arial"; + fontSize = 14; + + fontColor = "0 0 0"; + fontColorSEL = "43 107 206"; + fontColorHL = "244 244 244"; + fontColorNA = "100 100 100"; + + border = 1; + borderColor = "153 222 253 255"; + borderColorHL = "156 156 156"; + borderColorNA = "153 222 253 0"; + + //bevelColorHL = "255 255 255"; + //bevelColorLL = "0 0 0"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiMenuBarProfile ) +{ + fillcolor = "255 255 255"; + fillcolorHL = "213 231 248"; + borderColor = "98 163 229"; + borderColorHL = "122 177 232"; + border = 0; + borderThickness = 1; + opaque = true; + mouseOverSelected = true; + category = "Editor"; + bitmap = "tools/gui/images/checkbox-menubar"; +}; diff --git a/Templates/BaseGame/game/tools/gui/saveChangesMBDlg.ed.gui b/Templates/BaseGame/game/tools/gui/saveChangesMBDlg.ed.gui new file mode 100644 index 000000000..ec1a3e4ff --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/saveChangesMBDlg.ed.gui @@ -0,0 +1,180 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MessageBoxSaveChangesDlg, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(MBSaveChangesFrame) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "362 274"; + Extent = "340 164"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Save Changes"; + maxLength = "255"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + + new GuiIconButtonCtrl(mbSaveDlgSaveButton) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "240 117"; + Extent = "83 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Save"; + groupNum = "-1"; + buttonType = "PushButton"; + iconBitmap = "~/levelEditor/gui/images/iconAccept.png"; + sizeIconToButton = "0"; + textLocation = "Center"; + textMargin = "4"; + buttonMargin = "4 4"; + }; + new GuiIconButtonCtrl(mbSaveDlgCancelButton) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "158 117"; + Extent = "83 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + iconBitmap = "~/levelEditor/gui/images/iconCancel.png"; + sizeIconToButton = "0"; + textLocation = "Center"; + textMargin = "4"; + buttonMargin = "4 4"; + }; + new GuiIconButtonCtrl(mbSaveDlgDontButton) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "14 117"; + Extent = "124 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Don\'t Save"; + groupNum = "-1"; + buttonType = "PushButton"; + sizeIconToButton = "0"; + textLocation = "Center"; + textMargin = "4"; + buttonMargin = "4 4"; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "13 31"; + Extent = "310 73"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "EditorTextHLBoldLeft"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "15 9"; + Extent = "281 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Do you want to save changes to this document?"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "15 38"; + Extent = "258 21"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "If you don\'t save, your changes may be lost."; + maxLength = "1024"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +function MessageBoxSaveChangesDlg::onWake( %this ) +{ + MBSaveChangesFrame.setText( %this.Data ); +} + +function mbSaveDlgSaveButton::onClick( %this ) +{ + if( MessageBoxSaveChangesDlg.SaveCallback !$= "" ) + eval( MessageBoxSaveChangesDlg.SaveCallback @ "(" @ MessageBoxSaveChangesDlg.Data @ ");" ); + Canvas.popDialog( MessageBoxSaveChangesDlg ); +} + +function mbSaveDlgCancelButton::onClick( %this ) +{ + Canvas.popDialog( MessageBoxSaveChangesDlg ); +} + +function mbSaveDlgDontButton::onClick( %this ) +{ + if( MessageBoxSaveChangesDlg.DontSaveCallback !$= "" ) + eval( MessageBoxSaveChangesDlg.DontSaveCallback @ "(" @ MessageBoxSaveChangesDlg.Data @ ");" ); + Canvas.popDialog( MessageBoxSaveChangesDlg ); +} + +// Deprecated when platform layers are all sufficient +function checkSaveChangesOld( %data, %saveCallback, %dontSaveCallback ) +{ + // Sanity Check + if( MessageBoxSaveChangesDlg.isAwake() ) + { + warn("Save Changes Dialog already Awake, NOT creating second instance."); + return; + } + + // Set Proper State + MessageBoxSaveChangesDlg.SaveCallback = %saveCallback; + MessageBoxSaveChangesDlg.DontSaveCallback = %dontSaveCallback; + MessageBoxSaveChangesDlg.Data = %data; + + // Show Dialog + Canvas.pushDialog( MessageBoxSaveChangesDlg ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/gui/saveFileDialog.ed.cs b/Templates/BaseGame/game/tools/gui/saveFileDialog.ed.cs new file mode 100644 index 000000000..8756c2eab --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/saveFileDialog.ed.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function getSaveFilename( %filespec, %callback, %currentFile, %overwrite ) +{ + if( %overwrite $= "" ) + %overwrite = true; + + %dlg = new SaveFileDialog() + { + Filters = %filespec; + DefaultFile = %currentFile; + ChangePath = false; + OverwritePrompt = %overwrite; + }; + + if( filePath( %currentFile ) !$= "" ) + %dlg.DefaultPath = filePath( %currentFile ); + else + %dlg.DefaultPath = getMainDotCSDir(); + + if( %dlg.Execute() ) + { + %filename = %dlg.FileName; + eval( %callback @ "(\"" @ %filename @ "\");" ); + } + + %dlg.delete(); +} diff --git a/Templates/BaseGame/game/tools/gui/scriptEditorDlg.ed.gui b/Templates/BaseGame/game/tools/gui/scriptEditorDlg.ed.gui new file mode 100644 index 000000000..852d347a9 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/scriptEditorDlg.ed.gui @@ -0,0 +1,210 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ScriptEditorDlg,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "1"; + canMaximize = "1"; + minSize = "50 50"; + closeCommand = "ScriptEditorDlg.close();"; + EdgeSnap = "1"; + text = "Text Pad"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "176 120"; + Extent = "656 464"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ScriptEditorDlg.close();"; + Accelerator = "escape"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 24"; + Extent = "640 392"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 7"; + Extent = "627 380"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "0"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "1 1"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "627 380"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiMLTextEditCtrl() { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextPadProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "2 2"; + Extent = "623 380"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "TextPad"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiIconButtonCtrl() { + buttonMargin = "4 4"; + iconBitmap = "tools/gui/images/iconCancel.png"; + iconLocation = "Left"; + sizeIconToButton = "0"; + makeIconSquare = "0"; + textLocation = "Center"; + textMargin = "4"; + autoSize = "0"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "460 424"; + Extent = "80 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ScriptEditorDlg.close();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiIconButtonCtrl() { + buttonMargin = "4 4"; + iconBitmap = "tools/gui/images/iconAccept.png"; + iconLocation = "Left"; + sizeIconToButton = "0"; + makeIconSquare = "0"; + textLocation = "Center"; + textMargin = "4"; + autoSize = "0"; + text = "Ok"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "560 424"; + Extent = "80 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "_TextPadOnOk();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function TextPad(%text, %callback, %root) +{ + ScriptEditorDlg-->textpad.setText(%text); + ScriptEditorDlg.callback = %callback; + + if(!isObject(%root)) + %root = Canvas; + + %root.pushDialog(ScriptEditorDlg); +} + +function _TextPadOnOk() +{ + if(ScriptEditorDlg.callback !$= "") + { + %text = ScriptEditorDlg-->textpad.getText(); + %command = ScriptEditorDlg.callback @ "( %text );"; + eval(%command); + } + ScriptEditorDlg.callback = ""; + ScriptEditorDlg.getRoot().popDialog(ScriptEditorDlg); +} + +function ScriptEditorDlg::close(%this) +{ + %this.getRoot().popDialog(%this); +} diff --git a/Templates/BaseGame/game/tools/gui/simViewDlg.ed.gui b/Templates/BaseGame/game/tools/gui/simViewDlg.ed.gui new file mode 100644 index 000000000..14bc25b57 --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/simViewDlg.ed.gui @@ -0,0 +1,348 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(simViewDlg, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "70 43"; + Extent = "685 489"; + MinExtent = "602 440"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Torque SimView"; + maxLength = "1024"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "1"; + canMaximize = "1"; + minSize = "50 50"; + closeCommand = "Canvas.popDialog(simViewDlg);"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "10 28"; + Extent = "255 448"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTreeViewCtrl(InspectTreeView) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "212 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "left"; + VertSizing = "height"; + position = "272 96"; + Extent = "404 380"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(InspectFields) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "382 8"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + }; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "272 28"; + Extent = "403 61"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextEditCtrl(InspectObjectName) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "121 8"; + Extent = "195 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "EditorTextHLRight"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "217 35"; + Extent = "44 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Sim ID:"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "EditorTextHLRight"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 35"; + Extent = "106 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Internal Name:"; + maxLength = "1024"; + }; + new GuiTextEditCtrl(InspectObjectInternalName) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "121 35"; + Extent = "93 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "EditorTextHLBoldRight"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 8"; + Extent = "106 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Selected Object:"; + maxLength = "1024"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "321 33"; + Extent = "76 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "InspectApply();"; + hovertime = "1000"; + text = "Refresh"; + groupNum = "-1"; + buttonType = "PushButton"; + iconBitmap = "./images/iconRefresh.png"; + sizeIconToButton = "0"; + textLocation = "Right"; + textMargin = "4"; + buttonMargin = "4 4"; + }; + new GuiTextCtrl(InspectObjectSimID) { + canSaveDynamicFields = "0"; + Profile = "EditorTextHLBoldCenter"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "265 35"; + Extent = "51 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "0"; + maxLength = "1024"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "321 6"; + Extent = "76 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "InspectDelete();"; + hovertime = "1000"; + text = "Delete"; + groupNum = "-1"; + buttonType = "PushButton"; + iconBitmap = "./images/iconDelete.png"; + sizeIconToButton = "0"; + textLocation = "Right"; + textMargin = "4"; + buttonMargin = "4 4"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +function Inspect(%obj) +{ + // Don't inspect the root group. + if( %obj == -1 ) + return; + + InspectFields.inspect(%obj); + + // Update selected object properties + InspectObjectName.setValue(%obj.getName()); + InspectObjectInternalName.setValue( %obj.getInternalName() ); + InspectObjectSimID.setValue( %obj.getId() ); + + // Store Object Reference + InspectObjectName.refObj = %obj; + +} + +function InspectApply() +{ + %obj = InspectObjectName.refObj; + if( !isObject( %obj ) ) + return; + + // Update name and internal name + %obj.setName( InspectObjectName.getValue() ); + %obj.setInternalName( InspectObjectInternalName.getValue() ); + + // Update inspected object information. + InspectFields.inspect( %obj ); +} + +function InspectDelete() +{ + %obj = InspectObjectName.refObj; + if( !isObject( %obj ) ) + return; + + %obj.delete(); + + // Update inspected object information. + InspectFields.inspect( 0 ); + + // Update selected object properties + InspectObjectName.setValue(""); + InspectObjectInternalName.setValue( "" ); + InspectObjectSimID.setValue( 0 ); + + +} + + +function InspectTreeView::onSelect(%this, %obj) +{ + Inspect(%obj); +} + +function Tree(%obj) +{ + Canvas.popDialog("simViewDlg"); + Canvas.pushDialog("simViewDlg", 20); + InspectTreeView.open(%obj); +} + +// MM: Added Dynamic group toggle support. +function GuiInspector::toggleDynamicGroupScript(%this, %obj) +{ + %this.toggleDynamicGroupExpand(); + %this.inspect(%obj); +} +// MM: Added group toggle support. +function GuiInspector::toggleGroupScript(%this, %obj, %fieldName) +{ + %this.toggleGroupExpand(%obj, %fieldName); + %this.inspect(%obj); +} + +// MM: Set All Group State support. +function GuiInspector::setAllGroupStateScript(%this, %obj, %groupState) +{ + %this.setAllGroupState(%groupState); + %this.inspect(%obj); +} diff --git a/Templates/BaseGame/game/tools/gui/uvEditor.ed.gui b/Templates/BaseGame/game/tools/gui/uvEditor.ed.gui new file mode 100644 index 000000000..c27f8498e --- /dev/null +++ b/Templates/BaseGame/game/tools/gui/uvEditor.ed.gui @@ -0,0 +1,645 @@ +new GuiControl(UVEditorOverlay, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiOverlayProfile"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(UVEditor){ + profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + resizeWidth = "0"; + resizeHeight = "0"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + position = "72 98"; + extent =" 453 340"; + MinExtent = "453 340"; + text = "UV Editor"; + closeCommand = "UVEditor.hideDialog();"; + EdgeSnap = "0"; + canCollapse = "0"; + visible = "0"; + + new GuiTextCtrl() { + text = "0.0"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "26 24"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "U"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "138 24"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1.0"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "250 24"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + + new GuiTextCtrl() { + text = "0.0"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 36"; + Extent = "18 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "V"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 159"; + Extent = "18 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "1.0"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 282"; + Extent = "18 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + + new GuiControl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiSolidDefaultProfile"; + position = "25 37"; + extent = "258 258"; + }; + + new GuiBitmapCtrl(){ + internalName = "bitmapPreview"; + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + position = "26 38"; + extent = "256 256"; + wrap = "0"; + bitmap = ""; + }; + new GuiRectHandles(){ + internalName = "uvHandles"; + class = "UVEditorRectHandles"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "26 38"; + extent = "256 256"; + }; + + new GuiBitmapBorderCtrl() { + profile = "ToolsGuiGroupBorderProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "26 300"; + extent = "256 30"; + minExtent = "0 0"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + + new GuiTextCtrl() { + text = "Handle Color:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 7"; + Extent = "70 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopupMenuCtrlEx(){ + internalName = "colorPopup"; + Profile = "ToolsGuiPopUpMenuProfile"; + Position = "80 5"; + Extent = "126 20"; + HorizSizing = "right"; + VertSizing = "bottom"; + Command = "UVEditor.onColorSelect();"; + reverseTextList = "0"; + }; + }; + + new GuiBitmapBorderCtrl() { + profile = "ToolsGuiGroupBorderProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "292 38"; + extent = "151 256"; + minExtent = "0 0"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + + new GuiTextCtrl() { + text = "U:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 12"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + internalName = "UVX"; + class = "UVEditorUVTextEdit"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "44 10"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextCtrl() { + text = "V:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 32"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + internalName = "UVY"; + class = "UVEditorUVTextEdit"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "44 30"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextCtrl() { + text = "Width:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 52"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + internalName = "UVW"; + class = "UVEditorUVTextEdit"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "44 50"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextCtrl() { + text = "Height:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 72"; + Extent = "32 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + internalName = "UVH"; + class = "UVEditorUVTextEdit"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "44 70"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiButtonCtrl(){ + HorizSizing = "right"; + VertSizing = "top"; + profile = "ToolsGuiButtonProfile"; + position = "44 94"; + extent = "64 20"; + text = "Reset"; + command = "UVEditor.reset();"; + tooltip = "Reset the UV fields to their original values."; + }; + }; + new GuiButtonCtrl(){ + internalName = "OKButton"; + HorizSizing = "left"; + VertSizing = "top"; + profile = "ToolsGuiButtonProfile"; + position = "292 306"; + extent = "94 24"; + text = "OK"; + command = "UVEditor.apply();"; + Accelerator = "return"; + }; + new GuiButtonCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + profile = "ToolsGuiButtonProfile"; + position = "391 306"; + extent = "52 24"; + text = "Cancel"; + command = "UVEditor.hideDialog();"; + Accelerator = "escape"; + }; + }; +}; + +//----------------------------------------------------------------------------- + +function UVEditor::showDialog( %this, %applyCallback, %obj, %uv) +{ + // Set the select callback + UVEditor.applyCallback = %applyCallback; + + // Set the initial UV coordinates + UVEditor.originalUV = %uv; + UVEditor-->uvHandles.handleRect = %uv; + UVEditor.setTextValues(%uv); + + // Get the preview bitmap. Code copied from Material Selector. + %material = %obj.material; + if( %material.toneMap[0] $= "" && %material.diffuseMap[0] $= "" && !isObject(%material.cubemap) ) + { + %previewImage = "core/art/warnmat"; + } + else + { + if( %material.toneMap[0] !$= "" ) + %previewImage = %material.toneMap[0]; + else if( %material.diffuseMap[0] !$= "" ) + %previewImage = %material.diffuseMap[0]; + else if( %material.cubemap.cubeFace[0] !$= "" ) + %previewImage = %material.cubemap.cubeFace[0]; + + %materialDiffuse = %previewImage; + %materialPath = %material.getFilename(); + if( strchr( %materialDiffuse, "/") $= "" ) + { + %k = 0; + while( strpos( %materialPath, "/", %k ) != -1 ) + { + %foo = strpos( %materialPath, "/", %k ); + %k = %foo + 1; + } + + %foobar = getSubStr( %materialPath , %k , 99 ); + %previewImage = strreplace( %materialPath, %foobar, %previewImage ); + } + else + %previewImage = strreplace( %materialPath, %materialPath, %previewImage ); + } + + UVEditor-->bitmapPreview.setBitmap(%previewImage); + + // Set up the color popup + %popup = UVEditor-->colorPopup; + %popup.clear(); + %popup.add("Default1|255|134|0"); + %popup.add("Default2|0|121|255"); + %popup.add("Black|0|0|0"); + %popup.add("Gray|100|100|100"); + %popup.add("White|255|255|255"); + %popup.add("Red|255|0|0"); + %popup.add("Green|0|255|0"); + %popup.add("Blue|0|0|255"); + %popup.add("Yellow|255|255|0"); + %popup.add("Magenta|255|0|255"); + %popup.add("Cyan|0|255|255"); + %popup.setSelected(EditorSettings.value("WorldEditor/Color/uvEditorHandleColor")); + UVEditor-->uvHandles.useCustomColor = true; + UVEditor-->uvHandles.handleColor = %popup.getColorById(%popup.getSelected()); + + Canvas.pushDialog(UVEditorOverlay); + UVEditor.setVisible(1); +} + +function UVEditor::hideDialog( %this ) +{ + UVEditor.setVisible(0); + Canvas.popDialog(UVEditorOverlay); +} + +function UVEditor::apply( %this ) +{ + eval( "" @ UVEditor.applyCallback @ "(\"" @ UVEditor-->uvHandles.handleRect @ "\");"); + UVEditor.hideDialog(); +} + +function UVEditor::reset( %this ) +{ + UVEditor-->uvHandles.handleRect = UVEditor.originalUV; + UVEditor.setTextValues(UVEditor.originalUV); +} + +function UVEditor::setTextValues( %this, %uv ) +{ + UVEditor-->UVX.setText( getWord(%uv, 0) ); + UVEditor-->UVY.setText( getWord(%uv, 1) ); + UVEditor-->UVW.setText( getWord(%uv, 2) ); + UVEditor-->UVH.setText( getWord(%uv, 3) ); +} + +function UVEditor::onColorSelect( %this ) +{ + UVEditor-->uvHandles.useCustomColor = true; + %sel = $ThisControl.getSelected(); + UVEditor-->uvHandles.handleColor = $ThisControl.getColorById(%sel); + EditorSettings.setValue( "WorldEditor/Color/uvEditorHandleColor", %sel ); +} + +//----------------------------------------------------------------------------- + +function UVEditorRectHandles::onHandleRectChange( %this ) +{ + %uv = UVEditor-->uvHandles.handleRect; + UVEditor.setTextValues(%uv); +} + +//----------------------------------------------------------------------------- + +function UVEditorUVTextEdit::onValidate( %this ) +{ + %u = UVEditor-->UVX.getValue(); + %v = UVEditor-->UVY.getValue(); + %w = UVEditor-->UVW.getValue(); + %h = UVEditor-->UVH.getValue(); + + // Check limits + + if(%u < 0) + %u = 0; + if(%u > 1) + %u = 1; + if(%v < 0) + %v = 0; + if(%v > 1) + %v = 1; + + if(%w < 0) + %w = 0; + if(%w > 1) + %w = 1; + if(%h < 0) + %h = 0; + if(%h > 1) + %h = 1; + + if((%u+%w) > 1) + %w = 1 - %u; + if((%v+%h) > 1) + %h = 1 - %v; + + // Apply values + UVEditor-->UVX.setText( %u ); + UVEditor-->UVY.setText( %v ); + UVEditor-->UVW.setText( %w ); + UVEditor-->UVH.setText( %h ); + + UVEditor-->uvHandles.handleRect = %u SPC %v SPC %w SPC %h; +} + +function UVEditorUVTextEdit::onGainFirstResponder( %this ) +{ + %this.selectAllText(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/EditorChooseGUI.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/EditorChooseGUI.ed.gui new file mode 100644 index 000000000..02d9e04b3 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/gui/EditorChooseGUI.ed.gui @@ -0,0 +1,229 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiChunkedBitmapCtrl(EditorChooseGUI, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiContentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "art/gui/background"; + useVariable = "0"; + tile = "0"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "476 191"; + Extent = "211 351"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "GUI Selector"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 23"; + Extent = "188 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1: Edit an Existing GUI"; + maxLength = "255"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "46 317"; + Extent = "120 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "GE_ReturnToMainMenu();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Play Game"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 41"; + Extent = "192 200"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOn"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "4 0"; + + new GuiMLTextCtrl(GE_GUIList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiMLTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 1"; + Extent = "169 560"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "1"; + }; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "112 267"; + Extent = "90 23"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GE_OpenGUIFile();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Browse"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 244"; + Extent = "183 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "2: Create New or Open Existing GUI"; + maxLength = "255"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 267"; + Extent = "93 23"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GuiEditorNewGuiDialog.init( \"NewGui\", \"GuiControl\" );" @ "Canvas.pushdialog(GuiEditorNewGuiDialog);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "New GUI"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 294"; + Extent = "183 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "3: Play Game from Start"; + maxLength = "255"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/gridTiny2.PNG b/Templates/BaseGame/game/tools/guiEditor/gui/gridTiny2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8a2d1bd89b64232a1dac323df9a1d08ff48fa070 GIT binary patch literal 901 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSEa{HEjtmSN`?>!lvI6<+C7!;n z?AKU1xj6Ke9G+guz`(5O>Eakt5%>1)#oWUN0xW?`g{Qv?ikW5YxnPR{i_fn(hI%e` z@9NFfDo_8|%s+o$`;V<$f5F$%z4zaj?EUrjU;K84Q_sIv{hj8&vNk?C>;84W_5KWp zr5hMH8dyc7E$%i6Ffam<llYvz1D`(ZPvlBubo|4Tz-M14%fu+)z@cOL;0sR+P&EUG zz^(@+EYsTaxeQq6*t5QPaP#&}aiEezH%j<z|Nd4!@Ily@#ere7Z9x^A69bDwgA=2o z!5XEy!(LKARaM&;Tu47!&1=-aqrf~xFNIO3fpv<W#@+3Qn3rFl&X~d=xgcP{h5UBI z8#50)lx$<*II!?S)5j!v>y`+hp+M6vur62LVDm>dsAlhf9fgkh412^U@Ll0*tXro0 zR(Xwr0Cxi8vIZfRY!2<Mf(~paC0kfGFo*>hoMCEvzg(DsQAJ+wOQaB!fB1*x`43)x zmF1N7Ghk9+NN4Ki-PG`~*aYarTNgCHL~fj|!F+<j$l;}v*a4;phnG#4YPD?&s%j-2 z*d{P?v2y(lTl4h#FHmqaFf8QOYC3gRoawjy{xo2y-B)l(XKG)c3=B>Sk*K0GFD2PP zK`Wq8a_}XG4u5_7)IC-IBOL_n87s{7_X=+~Y{kT+&>#>|<nZ-DP<1P-A_J2G&|Vga zeKUdKH$kn@NnY}R#H=Ibm%TaW$T9hU-{7zO<;yQW7T)s)z%cP+6_-w7JnSsOD9|w7 z*;`!X&$|tCqxSvoYMm1m&mqFpdcbMnt;VI_xR{in1{<)>VDb{5@-F&e<K@?<8Gum{ zpb_vQUG_j@U(JHWTo)QAJXD+p4Ec>8nl4U~FPo8sk~YdE9>4mV4o)3?|9{BzGaQ~? z#M!_g)C!Ed0}>Hxja@)rxN^9HBI{BsheFFzfv_XTnbtU~eEj?0;mAvl-zDsG<K{E8 zMN0r(?7Z+OTjYTcma~BN&30VM%~e-!5gZP5z2&}Z;!Ld$iW>q}lqIa(+tdIG>wsMX zj6T5ly~MiocUZ-PFTcRaNQ6t|S0wv}`SZVjUd`~IL3U+bczRJnBQPH^c)I$ztaD0e F0sv#hR(k*d literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui new file mode 100644 index 000000000..cc03cbd07 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditor.ed.gui @@ -0,0 +1,1535 @@ +//--------------------------------------------------------------------------------------------- +// Torque Game Builder +// Copyright (C) GarageGames.com, Inc. +//--------------------------------------------------------------------------------------------- + +%guiContent = new GuiControl(GuiEditorGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiFrameSetCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiFrameSetProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 583"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + columns = "0 631"; + rows = "0"; + borderWidth = "1"; + border = "0"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + autoBalance = "1"; + fudgeFactor = "3"; + + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "627 583"; + MinExtent = "64 64"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiContainer(GHToolBar) { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "menubarProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "16000 32"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl(GHWorldEditor) { + bitmap = "tools/worldEditor/images/toolbar/world"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "4 3"; + extent = "29 27"; + minExtent = "8 8"; + canSave = "1"; + visible = "1"; + command = "toggleEditor(1);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "World Editor"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl(GHGuiEditor) { + bitmap = "tools/worldEditor/images/toolbar/gui"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "34 3"; + extent = "29 27"; + minExtent = "8 8"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Gui Editor"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/worldEditor/images/toolbar/playbutton"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "64 3"; + extent = "29 27"; + minExtent = "8 8"; + canSave = "1"; + visible = "1"; + command = "GuiEdit(); Editor.close(\"PlayGui\");"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Play Game"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "98 3"; + extent = "2 26"; + minExtent = "1 1"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "99 0"; + extent = "723 32"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiPopUpMenuCtrl(GuiEditorContentList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "NewGui - 8844"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiPopUpMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "8 7"; + extent = "145 18"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl(GuiEditorResList) { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "1024 x 768 (XGA, 4:3)"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiPopUpMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "161 7"; + extent = "136 18"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "307 3"; + extent = "2 26"; + minExtent = "1 1"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "312 3"; + extent = "95 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl(GuiEditorSnapCheckBox) { + bitmap = "tools/gui/images/GUI-editor/snap-grid"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Snap gui controls to a grid. Modify grid size under edit."; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl(GuiEditorEdgeSnapping_btn) { + bitmap = "tools/gui/images/GUI-editor/edgesnap"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "31 0"; + extent = "29 27"; + minExtent = "120 21"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.toggleEdgeSnap();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles Edge Smart Snapping"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl(GuiEditorCenterSnapping_btn) { + bitmap = "tools/gui/images/GUI-editor/centersnap"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "62 0"; + extent = "29 27"; + minExtent = "120 21"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.toggleCenterSnap();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles Center Smart Snapping"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "415 3"; + extent = "2 26"; + minExtent = "1 1"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "422 3"; + extent = "95 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/align-left"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "29 27"; + minExtent = "120 21"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(0);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Align Left"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/vertical-center"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(1);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Center Vertically "; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/align-right"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "40 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(2);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Align Right"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "498 3"; + extent = "95 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/align-top"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "29 27"; + minExtent = "120 21"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(3);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Align Top"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/horizontal-center"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "25 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(7);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Center Horizontally"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/align-bottom"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "50 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(4);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Align Bottom"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "582 3"; + extent = "2 26"; + minExtent = "1 1"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-h.png"; + wrap = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "639 3"; + extent = "2 26"; + minExtent = "1 1"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "615 3"; + extent = "117 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/send-to-back"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "49 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.PushToBack();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Send to Back"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/bring-to-front"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "27 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.BringToFront();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Send to Front"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "583 3"; + extent = "60 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/distribute-horizontal"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(6);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Distribute Horizontally"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/GUI-editor/distribute-vertical"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "25 0"; + extent = "29 27"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditor.Justify(5);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + ToolTip = "Distribute Vertically"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + //--------------------- + new GuiEditorRuler(GuiEditorTopRuler) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "10 32"; + Extent = "614 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + refCtrl = "GuiEditorScroll"; + editCtrl = "GuiEditor"; + }; + new GuiEditorRuler(GuiEditorLeftRuler) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "height"; + Position = "0 42"; + Extent = "10 523"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + refCtrl = "GuiEditorScroll"; + editCtrl = "GuiEditor"; + }; + + new GuiScrollCtrl(GuiEditorScroll) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "10 41"; + Extent = "617 543"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiControl(GuiEditorRegion) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "1 1"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiBackFillProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/guiEditor/gui/gridTiny2"; + wrap = "1"; + }; + new GuiControl(GuiEditorContent) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + }; + new GuiEditCtrl(GuiEditor) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Docking = "None"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + }; + }; + }; + }; + new GuiControl(GuiEditorSidebar) { + isContainer = "1"; + Profile = "menubarProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "798 0"; + Extent = "226 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTabBookCtrl(GuiEditorTabBook) { + tabPosition = "Top"; + tabMargin = "7"; + minTabWidth = "40"; + tabHeight = "20"; + allowReorder = "1"; + defaultPage = "0"; + selectedPage = "0"; + FrontTabPadding = "0"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 12"; + Extent = "223 754"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl() { + fitBook = "1"; + text = "GUI"; + maxLength = "1024"; + docking = "client"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 20"; + Extent = "223 734"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + internalName = "guiPage"; + + new GuiFrameSetCtrl() { + columns = "0"; + rows = "0 338"; + borderWidth = "1"; + borderColor = "207 207 207 207"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + autoBalance = "1"; + fudgeFactor = "2"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiFrameSetProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "222 734"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiPanel() { + position = "0 0"; + extent = "222 337"; + + new GuiTextEditCtrl( GuiEditorTreeFilter ) { + position = "2 4"; + extent = "200 18"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + class = "GuiTreeViewFilterText"; + treeView = GuiEditorTreeView; + }; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "205 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + class = "GuiTreeViewFilterClearButton"; + textCtrl = GuiEditorTreeFilter; + }; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 25"; + Extent = "222 312"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl(GuiEditorTreeView) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "0"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + ClearAllOnSingleSelection = "1"; + showRoot = "1"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 22"; + Extent = "89 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + showObjectIds = "0"; + showClassNames = "0"; + showObjectNames = "1"; + showInternalNames = "1"; + showClassNameForUnnamedObjects = "1"; + }; + }; + }; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 338"; + Extent = "222 396"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 2"; + Extent = "223 341"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiInspector(GuiEditorInspectFields) { + dividerMargin = "5"; + showCustomFields = "1"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + DynamicSize = "1"; + ChangeChildSizeToFit = "1"; + ChangeChildPosition = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "221 24"; + MinExtent = "8 24"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + superClass = "EditorInspectorBase"; + }; + }; + new GuiMLTextCtrl(GuiEditorFieldInfo) { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + position = "0 349"; + Extent = "213 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + + new GuiTabPageCtrl() { + fitBook = "1"; + text = "Library"; + maxLength = "1024"; + docking = "client"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 20"; + Extent = "223 734"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + internalName = "toolboxPage"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + docking = "client"; + + new GuiStackControl(GuiEditorToolbox) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + DynamicSize = "1"; + ChangeChildSizeToFit = "1"; + ChangeChildPosition = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "3 3"; + Extent = "419 10008"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + + new GuiTabPageCtrl() { + fitBook = "1"; + text = "Profiles"; + maxLength = "1024"; + docking = "client"; + Margin = "-1 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 20"; + Extent = "223 734"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + internalName = "profilesPage"; + + new GuiFrameSetCtrl() { + columns = "0"; + rows = "0 338"; + borderWidth = "1"; + borderColor = "207 207 207 207"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + autoBalance = "1"; + fudgeFactor = "2"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiFrameSetProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "222 734"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiPanel() { + position = "0 0"; + extent = "222 337"; + + new GuiTextEditCtrl( GuiEditorProfilesTreeFilter ) { + position = "2 4"; + extent = "200 18"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + class = "GuiTreeViewFilterText"; + treeView = GuiEditorProfilesTree; + }; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "205 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + class = "GuiTreeViewFilterClearButton"; + textCtrl = GuiEditorProfilesTreeFilter; + }; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 25"; + Extent = "222 312"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl(GuiEditorProfilesTree) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "0"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "0"; + DragToItemAllowed = "0"; + ClearAllOnSingleSelection = "1"; + showRoot = "1"; + internalNamesOnly = "0"; + objectNamesOnly = "0"; + useInspectorTooltips = "0"; + tooltipOnWidthOnly = "0"; + compareToObjectID = "1"; + canRenameObjects = "1"; + renameInternal = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "89 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 338"; + Extent = "222 396"; + MinExtent = "8 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextEditCtrl(GuiEditorProfileFileName) { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "180 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/save-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "184 2"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GuiEditor.saveProfile( GuiEditorProfilesTree.getSelectedProfile(), GuiEditorProfileFileName.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Save the currently selected profile."; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/save-as"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "205 2"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GuiEditor.showSaveProfileDialog( GuiEditorProfileFileName.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Save the currently selected profile to a different file."; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 22"; + Extent = "223 321"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiInspector(GuiEditorProfileInspector) { + dividerMargin = "5"; + showCustomFields = "1"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + DynamicSize = "1"; + ChangeChildSizeToFit = "1"; + ChangeChildPosition = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "221 24"; + MinExtent = "8 24"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + superClass = "EditorInspectorBase"; + }; + }; + new GuiMLTextCtrl(GuiEditorProfileFieldInfo) { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + position = "0 349"; + Extent = "213 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + }; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "156 12"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "button1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "174 12"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "button2"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "192 12"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "button3"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "207 12"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "button4"; + canSaveDynamicFields = "0"; + }; + }; + }; + + new GuiControl() { + position = "0 583"; + extent = "800 17"; + horizSizing = "width"; + vertSizing = "top"; + minExtent = "64 17"; + canSave = "1"; + visible = "1"; + isContainer = "1"; + profile = "menubarProfile"; + + new GuiTextCtrl( GuiEditorStatusBar ) { + profile = "ToolsGuiTextProfile"; + position = "5 0"; + extent = "500 17"; + minExtent = "64 17"; + canSave = "1"; + visible = "1"; + }; + new GuiSeparatorCtrl() { + profile = "ToolsGuiDefaultProfile"; + position = "505 0"; + extent = "10 17"; + minExtent = "10 17"; + canSave = "1"; + visible = "1"; + horizSizing = "left"; + }; + new GuiTextCtrl( GuiEditorSelectionStatus ) { + profile = "ToolsGuiTextProfile"; + position = "515 0"; + extent = "100 17"; + minExtent = "100 17"; + canSave = "1"; + visible = "1"; + horizSizing = "left"; + }; + }; +}; diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorNewGuiDialog.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorNewGuiDialog.ed.gui new file mode 100644 index 000000000..98252410a --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorNewGuiDialog.ed.gui @@ -0,0 +1,199 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(GuiEditorNewGuiDialog,EditorGuiGroup) { + isContainer = "1"; + profile = "ToolsGuiOverlayProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + enabled = "1"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + edgeSnap = "0"; + text = "Create new GUI"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "357 303"; + extent = "310 161"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "228 114"; + extent = "63 25"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditorNewGuiDialog.onCancel();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Create"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "101 114"; + extent = "124 25"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + command = "GuiEditorNewGuiDialog.onOK();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + accelerator = "enter"; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "15 28"; + extent = "278 76"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "GUI Name"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "14 13"; + extent = "80 18"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "103 13"; + extent = "160 17"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "nameField"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "GUI Class"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "14 44"; + extent = "80 18"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiPopUpMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "103 44"; + extent = "160 18"; + minExtent = "8 2"; + canSave = "1"; + visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "classDropdown"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorPrefsDlg.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorPrefsDlg.ed.gui new file mode 100644 index 000000000..6f901d1a8 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorPrefsDlg.ed.gui @@ -0,0 +1,181 @@ +%guiContent = new GuiControl(GuiEditorPrefsDlg, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "250 210"; + Extent = "280 95"; + MinExtent = "344 144"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "Canvas.popDialog(\"GuiEditorPrefsDlg\");"; + EdgeSnap = "0"; + text = "Gui Editor Grid Preferences"; + + new GuiButtonCtrl(GuiEditorPrefsDlgCancelBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "100 60"; + Extent = "80 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Accelerator = "escape"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(GuiEditorPrefsDlgOkBtn) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "190 60"; + Extent = "80 25"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Ok"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(GuiEditorPrefsDlgDefaultsBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "10 60"; + Extent = "60 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Reset"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "center"; + VertSizing = "bottom"; + position = "-3 20"; + Extent = "288 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "16 10"; + Extent = "48 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Grid Size:"; + maxLength = "1024"; + }; + new GuiTextEditCtrl(GuiEditorPrefsDlgGridEdit) { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + Enabled = "1"; + Component = "textEdit"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 11"; + Extent = "32 18"; + MinExtent = "8 18"; + canSave = "1"; + Visible = "1"; + Command = "GuiEditorPrefsDlgGridEdit.onAction();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + new GuiSliderCtrl(GuiEditorPrefsDlgGridSlider) { + canSaveDynamicFields = "0"; + internalName = "Slider"; + Enabled = "1"; + Component = "Slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "112 14"; + Extent = "160 12"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "GuiEditorPrefsDlgGridSlider.onAction();"; + hovertime = "1000"; + range = "0 64"; + ticks = "0"; + value = "0"; + }; + }; + }; +}; + diff --git a/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorSelectDlg.ed.gui b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorSelectDlg.ed.gui new file mode 100644 index 000000000..1794a238d --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/gui/guiEditorSelectDlg.ed.gui @@ -0,0 +1,566 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(GuiEditorSelectDlgContainer,EditorGuiGroup) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl(GuiEditorSelectDlg) { + text = "Select Controls"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "1"; + closeCommand = "$ThisControl.toggleVisibility();"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "268 177"; + extent = "380 373"; + minExtent = "200 100"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + class = "EObjectSelection"; + internalName = "SelectControlsDlg"; + + new GuiBitmapBorderCtrl() { + position = "7 104"; + extent = "265 262"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 25"; + extent = "246 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "246 1242"; + minExtent = "16 16"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "classList"; + canSave = "1"; + canSaveDynamicFields = "0"; + + }; + }; + new GuiButtonCtrl() { + text = "Select All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "10 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "GuiEditorSelectDlg.selectAllInClassList( true );"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Classes"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "113 6"; + extent = "40 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "76 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "GuiEditorSelectDlg.selectAllInClassList( false );"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + position = "7 25"; + extent = "366 74"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Name Pattern"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 9"; + extent = "67 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Retain Current Selection"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "216 46"; + extent = "140 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "retainSelection"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Create Selection Set"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 73"; + extent = "117 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "createSelectionSet"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "•"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "157 80"; + extent = "199 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiTextEditProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "selectionSetName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "•"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "91 9"; + extent = "265 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "namePattern"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Select Child Controls Of:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 30"; + extent = "119 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "138 30"; + extent = "218 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "groupList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + position = "246 104"; + extent = "233 262"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "9 25"; + extent = "215 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "215 16"; + minExtent = "16 16"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "filterList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "Select All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "9 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Filters"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "101 6"; + extent = "30 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "75 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "Select"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "278 104"; + extent = "95 30"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "GuiEditorSelectDlg.onSelectObjects(true);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "278 137"; + extent = "95 30"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "GuiEditorSelectDlg.onSelectObjects(false);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/guiEditor/main.cs b/Templates/BaseGame/game/tools/guiEditor/main.cs new file mode 100644 index 000000000..da793b380 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/main.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeGuiEditor() +{ + echo( " % - Initializing Gui Editor" ); + + // GUIs. + + exec( "./gui/guiEditor.ed.gui" ); + exec( "./gui/guiEditorNewGuiDialog.ed.gui" ); + exec( "./gui/guiEditorPrefsDlg.ed.gui" ); + exec( "./gui/guiEditorSelectDlg.ed.gui" ); + exec( "./gui/EditorChooseGUI.ed.gui" ); + + // Scripts. + + exec( "./scripts/guiEditor.ed.cs" ); + exec( "./scripts/guiEditorTreeView.ed.cs" ); + exec( "./scripts/guiEditorInspector.ed.cs" ); + exec( "./scripts/guiEditorProfiles.ed.cs" ); + exec( "./scripts/guiEditorGroup.ed.cs" ); + exec( "./scripts/guiEditorUndo.ed.cs" ); + exec( "./scripts/guiEditorCanvas.ed.cs" ); + exec( "./scripts/guiEditorContentList.ed.cs" ); + exec( "./scripts/guiEditorStatusBar.ed.cs" ); + exec( "./scripts/guiEditorToolbox.ed.cs" ); + exec( "./scripts/guiEditorSelectDlg.ed.cs" ); + + exec( "./scripts/guiEditorNewGuiDialog.ed.cs" ); + exec( "./scripts/fileDialogs.ed.cs" ); + exec( "./scripts/guiEditorPrefsDlg.ed.cs" ); + exec( "./scripts/EditorChooseGUI.ed.cs" ); +} + +function destroyGuiEditor() +{ +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/EditorChooseGUI.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/EditorChooseGUI.ed.cs new file mode 100644 index 000000000..d59c61225 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/EditorChooseGUI.ed.cs @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function GE_ReturnToMainMenu() +{ + loadMainMenu(); +} + +function GE_OpenGUIFile() +{ + %openFileName = GuiBuilder::getOpenName(); + if( %openFileName $= "" ) + return; + + // Make sure the file is valid. + if ((!isFile(%openFileName)) && (!isFile(%openFileName @ ".dso"))) + return; + + // Allow stomping objects while exec'ing the GUI file as we want to + // pull the file's objects even if we have another version of the GUI + // already loaded. + + %oldRedefineBehavior = $Con::redefineBehavior; + $Con::redefineBehavior = "replaceExisting"; + + // Load up the level. + exec( %openFileName ); + + $Con::redefineBehavior = %oldRedefineBehavior; + + // The level file should have contained a scenegraph, which should now be in the instant + // group. And, it should be the only thing in the group. + if( !isObject( %guiContent ) ) + { + MessageBox( getEngineName(), + "You have loaded a Gui file that was created before this version. It has been loaded but you must open it manually from the content list dropdown", + "Ok", "Information" ); + GuiEditContent( Canvas.getContent() ); + return 0; + } + + GuiEditContent( %guiContent ); +} + +function GE_GUIList::onURL(%this, %url) +{ + // Remove 'gamelink:' from front + %gui = getSubStr(%url, 9, 1024); + GuiEditContent(%gui); +} + +function EditorChooseGUI::onWake() +{ + // Build the text list + GE_GUIList.clear(); + + %list = "<linkcolor:0000FF><linkcolorhl:FF0000>"; + %list = GE_ScanGroupForGuis(GuiGroup, %list); + GE_GUIList.setText(%list); + GE_GUIList.forceReflow(); + GE_GUIList.scrollToTop(); +} + +function GE_ScanGroupForGuis(%group, %text) +{ + %count = %group.getCount(); + for(%i=0; %i < %count; %i++) + { + %obj = %group.getObject(%i); + if(%obj.getClassName() $= "GuiCanvas") + { + %text = %text @ GE_ScanGroupForGuis(%obj, %text); + } + else + { + if(%obj.getName() $= "") + %name = "(unnamed) - " @ %obj; + else + %name = %obj.getName() @ " - " @ %obj; + + %text = %text @ "<a:gamelink:" @ %obj @ ">" @ %name @ "</a><br>"; + } + } + + return %text; +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/fileDialogs.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/fileDialogs.ed.cs new file mode 100644 index 000000000..dbb1a2a37 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/fileDialogs.ed.cs @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$GUI::FileSpec = "Torque Gui Files (*.gui)|*.gui|All Files (*.*)|*.*|"; + +/// GuiBuilder::getSaveName - Open a Native File dialog and retrieve the +/// location to save the current document. +/// @arg defaultFileName The FileName to default in the field and to be selected when a path is opened +function GuiBuilder::getSaveName( %defaultFileName ) +{ + %defaultPath = GuiEditor.LastPath; + + if( %defaultFileName $= "" ) + { + %prefix = ""; + if( isFunction( "isScriptPathExpando" ) ) + { + // if we're editing a game, we want to default to the games dir. + // if we're not, then we default to the tools directory or the base. + if( isScriptPathExpando( "^game") ) + %prefix = "^game/"; + else if( isScriptPathExpando( "^tools" ) ) + %prefix = "^tools/"; + } + + %defaultFileName = expandFilename( %prefix @ "gui/untitled.gui" ); + } + else + %defaultPath = filePath( %defaultFileName ); + + %dlg = new SaveFileDialog() + { + Filters = $GUI::FileSpec; + DefaultPath = makeFullPath( %defaultPath ); + DefaultFile = %defaultFileName; + ChangePath = false; + OverwritePrompt = true; + }; + + if( %dlg.Execute() ) + { + GuiEditor.LastPath = filePath( %dlg.FileName ); + %filename = %dlg.FileName; + if( fileExt( %filename ) !$= ".gui" ) + %filename = %filename @ ".gui"; + } + else + %filename = ""; + + %dlg.delete(); + + return %filename; +} + +function GuiBuilder::getOpenName( %defaultFileName ) +{ + if( %defaultFileName $= "" ) + %defaultFileName = expandFilename("^game/gui/untitled.gui"); + + %dlg = new OpenFileDialog() + { + Filters = $GUI::FileSpec; + DefaultPath = GuiEditor.LastPath; + DefaultFile = %defaultFileName; + ChangePath = false; + MustExist = true; + }; + + if(%dlg.Execute()) + { + GuiEditor.LastPath = filePath( %dlg.FileName ); + %filename = %dlg.FileName; + %dlg.delete(); + return %filename; + } + + %dlg.delete(); + return ""; +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.cs new file mode 100644 index 000000000..61dc8c5e7 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditor.ed.cs @@ -0,0 +1,1189 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//============================================================================================= +// Activation. +//============================================================================================= + +$InGuiEditor = false; +$MLAAFxGuiEditorTemp = false; + +function GuiEdit( %val ) +{ + if (Canvas.isFullscreen()) + { + MessageBoxOK("Windowed Mode Required", "Please switch to windowed mode to access the GUI Editor."); + return; + } + + if(%val != 0) + return; + + if (!$InGuiEditor) + { + GuiEditContent(Canvas.getContent()); + + //Temp fix to disable MLAA when in GUI editor + if( isObject(MLAAFx) && MLAAFx.isEnabled==true ) + { + MLAAFx.isEnabled = false; + $MLAAFxGuiEditorTemp = true; + } + + } + else + { + GuiEditCanvas.quit(); + } + +} + +function GuiEditContent( %content ) +{ + if( !isObject( GuiEditCanvas ) ) + new GuiControl( GuiEditCanvas, EditorGuiGroup ); + + GuiEditor.openForEditing( %content ); + + $InGuiEditor = true; +} + +function toggleGuiEditor( %make ) +{ + if( %make ) + { + if( EditorIsActive() && !GuiEditor.toggleIntoEditorGui ) + toggleEditor( true ); + + if( !isObject( GuiEditCanvas ) ) + new GuiControl( GuiEditCanvas, EditorGuiGroup ); + + if( GuiEditorIsActive() ) + { + GuiEditor.close(); + } + else + { + GuiEditor.open(); + + // Cancel the scheduled event to prevent + // the level from cycling after it's duration + // has elapsed. + cancel($Game::Schedule); + } + + // Cancel the scheduled event to prevent + // the level from cycling after it's duration + // has elapsed. + cancel($Game::Schedule); + } +} + +GlobalActionMap.bind( keyboard, "f10", toggleGuiEditor ); + +//============================================================================================= +// Methods. +//============================================================================================= + +package GuiEditor_BlockDialogs +{ + function GuiCanvas::pushDialog() {} + function GuiCanvas::popDialog() {} +}; + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::open(%this) +{ + GuiEditCanvas.onCreateMenu(); + + GuiEditContent(Canvas.getContent()); +} + +function GuiEditor::close(%this) +{ + // prevent the mission editor from opening while the GuiEditor is open. + if(Canvas.getContent() != GuiEditorGui.getId()) + return; + + GuiGroup.add(GuiEditorGui); + + Canvas.setContent(GuiEditor.lastContent); + + GuiEditCanvas.onDestroyMenu(); +} + +function GuiEditor::openForEditing( %this, %content ) +{ + Canvas.setContent( GuiEditorGui ); + while( GuiEditorContent.getCount() ) + GuiGroup.add( GuiEditorContent.getObject( 0 ) ); // get rid of anything being edited + + // Clear the current guide set and add the guides + // from the control. + + %this.clearGuides(); + %this.readGuides( %content ); + + // Enumerate GUIs and put them into the content list. + + GuiEditorContentList.init(); + + GuiEditorScroll.scrollToTop(); + activatePackage( GuiEditor_BlockDialogs ); + GuiEditorContent.add( %content ); + deactivatePackage( GuiEditor_BlockDialogs ); + GuiEditorContentList.sort(); + + if(%content.getName() $= "") + %name = "(unnamed) - " @ %content; + else + %name = %content.getName() @ " - " @ %content; + + GuiEditorContentList.setText(%name); + + %this.setContentControl(%content); + + // Initialize the preview resolution list and select the current + // preview resolution. + + GuiEditorResList.init(); + + %res = %this.previewResolution; + if( %res $= "" ) + %res = "1024 768"; + GuiEditorResList.selectFormat( %res ); + + // Initialize the treeview and expand the first level. + + GuiEditorTreeView.init(); + GuiEditorTreeView.open( %content ); + GuiEditorTreeView.expandItem( 1 ); + + // Initialize profiles tree. + + if( !GuiEditorProfilesTree.isInitialized ) + { + GuiEditorProfilesTree.init(); + GuiEditorProfilesTree.isInitialized = true; + } + + // Create profile change manager if we haven't already. + + if( !isObject( GuiEditorProfileChangeManager ) ) + new SimGroup( GuiEditorProfileChangeManager ); + + // clear the undo manager if we're switching controls. + if( %this.lastContent != %content ) + GuiEditor.getUndoManager().clearAll(); + + GuiEditor.setFirstResponder(); + + %this.updateUndoMenu(); + %this.lastContent = %content; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::switchToWorldEditor( %this ) +{ + %editingWorldEditor = false; + if( GuiEditorContent.getObject( 0 ) == EditorGui.getId() ) + %editingWorldEditor = true; + + GuiEdit(); + + if( !$missionRunning ) + EditorNewLevel(); + else if( !%editingWorldEditor ) + toggleEditor( true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::enableMenuItems(%this, %val) +{ + %menu = GuiEditCanvas.menuBar->EditMenu.getID(); + + %menu.enableItem( 3, %val ); // cut + %menu.enableItem( 4, %val ); // copy + %menu.enableItem( 5, %val ); // paste + //%menu.enableItem( 7, %val ); // selectall + //%menu.enableItem( 8, %val ); // deselectall + %menu.enableItem( 9, %val ); // selectparents + %menu.enableItem( 10, %val ); // selectchildren + %menu.enableItem( 11, %val ); // addselectparents + %menu.enableItem( 12, %val ); // addselectchildren + %menu.enableItem( 15, %val ); // lock + %menu.enableItem( 16, %val ); // hide + %menu.enableItem( 18, %val ); // group + %menu.enableItem( 19, %val ); // ungroup + + GuiEditCanvas.menuBar->LayoutMenu.enableAllItems( %val ); + GuiEditCanvas.menuBar->MoveMenu.enableAllItems( %val ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::showPrefsDialog(%this) +{ + Canvas.pushDialog(GuiEditorPrefsDlg); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::getUndoManager( %this ) +{ + if( !isObject( GuiEditorUndoManager ) ) + new UndoManager( GuiEditorUndoManager ); + + return GuiEditorUndoManager; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::undo(%this) +{ + %action = %this.getUndoManager().getNextUndoName(); + + %this.getUndoManager().undo(); + %this.updateUndoMenu(); + //%this.clearSelection(); + + GuiEditorStatusBar.print( "Undid '" @ %action @ "'" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::redo(%this) +{ + %action = %this.getUndoManager().getNextRedoName(); + + %this.getUndoManager().redo(); + %this.updateUndoMenu(); + //%this.clearSelection(); + + GuiEditorStatusBar.print( "Redid '" @ %action @ "'" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::updateUndoMenu(%this) +{ + %uman = %this.getUndoManager(); + %nextUndo = %uman.getNextUndoName(); + %nextRedo = %uman.getNextRedoName(); + + %editMenu = GuiEditCanvas.menuBar->editMenu; + + %editMenu.setItemName( 0, "Undo " @ %nextUndo ); + %editMenu.setItemName( 1, "Redo " @ %nextRedo ); + + %editMenu.enableItem( 0, %nextUndo !$= "" ); + %editMenu.enableItem( 1, %nextRedo !$= "" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::isFilteredClass( %this, %className ) +{ + // Filter out all the internal GuiInspector classes. + + if( startsWith( %className, "GuiInspector" ) && %className !$= "GuiInspector" ) + return true; + + // Filter out GuiEditor classes. + + if( startsWith( %className, "GuiEditor" ) ) + return true; + + // Filter out specific classes. + + switch$( %className ) + { + case "GuiCanvas": return true; + case "GuiAviBitmapCtrl": return true; // For now. Probably removed altogether. + case "GuiArrayCtrl": return true; // Abstract base class really. + case "GuiScintillaTextCtrl": return true; // Internal class. + case "GuiNoMouseCtrl": return true; // Too odd. + case "GuiEditCtrl": return true; + case "GuiBackgroundCtrl": return true; // Just plain useless. + case "GuiTSCtrl": return true; // Abstract base class. + case "GuiTickCtrl": return true; // Abstract base class. + case "GuiWindowCollapseCtrl": return true; // Legacy. + } + + return false; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::editProfile( %this, %profile ) +{ + GuiEditorTabBook->profilesPage.select(); + GuiEditorProfilesTree.setSelectedProfile( %profile ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::createControl( %this, %className ) +{ + %ctrl = eval( "return new " @ %className @ "();" ); + if( !isObject( %ctrl ) ) + return; + + // Add the control. + + %this.addNewCtrl( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +/// Group all GuiControls in the currenct selection set under a new GuiControl. +function GuiEditor::groupSelected( %this ) +{ + %selection = %this.getSelection(); + if( %selection.getCount() < 2 ) + return; + + // Create action. + + %action = GuiEditorGroupAction::create( %selection, GuiEditor.getContentControl() ); + %action.groupControls(); + + // Update editor tree. + + %this.clearSelection(); + %this.addSelection( %action.group[ 0 ].groupObject ); + GuiEditorTreeView.update(); + + // Update undo state. + + %action.addtoManager( %this.getUndoManager() ); + %this.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +/// Take all direct GuiControl instances in the selection set and reparent their child controls +/// to each of the group's parents. The GuiControl group objects are deleted. +function GuiEditor::ungroupSelected( %this ) +{ + %action = GuiEditorUngroupAction::create( %this.getSelection() ); + %action.ungroupControls(); + + // Update editor tree. + + %this.clearSelection(); + GuiEditorTreeView.update(); + + // Update undo state. + + %action.addToManager( %this.getUndoManager() ); + %this.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::deleteControl( %this, %ctrl ) +{ + // Unselect. + + GuiEditor.removeSelection( %ctrl ); + + // Record undo. + + %set = new SimSet() { parentGroup = RootGroup; }; + %set.add( %ctrl ); + + %action = UndoActionDeleteObject::create( %set, %this.getTrash(), GuiEditorTreeView ); + %action.addToManager( %this.getUndoManager() ); + %this.updateUndoMenu(); + + GuiEditorTreeView.update(); + %set.delete(); + + // Remove. + + %this.getTrash().add( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::setPreviewResolution( %this, %width, %height ) +{ + GuiEditorRegion.resize( 0, 0, %width, %height ); + GuiEditorContent.getObject( 0 ).resize( 0, 0, %width, %height ); + + GuiEditor.previewResolution = %width SPC %height; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleEdgeSnap( %this ) +{ + %this.snapToEdges = !%this.snapToEdges; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_EDGESNAP_INDEX, %this.snapToEdges ); + GuiEditorEdgeSnapping_btn.setStateOn( %this.snapToEdges ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleCenterSnap( %this ) +{ + %this.snapToCenters = !%this.snapToCenters; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CENTERSNAP_INDEX, %this.snapToCenters ); + GuiEditorCenterSnapping_btn.setStateOn( %this.snapToCenters ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleFullBoxSelection( %this ) +{ + %this.fullBoxSelection = !%this.fullBoxSelection; + GuiEditCanvas.menuBar->EditMenu.checkItem( $GUI_EDITOR_MENU_FULLBOXSELECT_INDEX, %this.fullBoxSelection ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleDrawGuides( %this ) +{ + %this.drawGuides= !%this.drawGuides; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_DRAWGUIDES_INDEX, %this.drawGuides ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleGuideSnap( %this ) +{ + %this.snapToGuides = !%this.snapToGuides; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_GUIDESNAP_INDEX, %this.snapToGuides ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleControlSnap( %this ) +{ + %this.snapToControls = !%this.snapToControls; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CONTROLSNAP_INDEX, %this.snapToControls ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleCanvasSnap( %this ) +{ + %this.snapToCanvas = !%this.snapToCanvas; + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CANVASSNAP_INDEX, %this.snapToCanvas ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleGridSnap( %this ) +{ + %this.snap2Grid = !%this.snap2Grid; + if( !%this.snap2Grid ) + %this.setSnapToGrid( 0 ); + else + %this.setSnapToGrid( %this.snap2GridSize ); + + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_GRIDSNAP_INDEX, %this.snap2Grid ); + GuiEditorSnapCheckBox.setStateOn( %this.snap2Grid ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleLockSelection( %this ) +{ + %this.toggleFlagInAllSelectedObjects( "locked" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleHideSelection( %this ) +{ + %this.toggleFlagInAllSelectedObjects( "hidden" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::selectAllControlsInSet( %this, %set, %deselect ) +{ + if( !isObject( %set ) ) + return; + + foreach( %obj in %set ) + { + if( !%obj.isMemberOfClass( "GuiControl" ) ) + continue; + + if( !%deselect ) + %this.addSelection( %obj ); + else + %this.removeSelection( %obj ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::toggleFlagInAllSelectedObjects( %this, %flagFieldName ) +{ + // Use the inspector's code here to record undo information + // for the field edits. + + GuiEditorInspectFields.onInspectorPreFieldModification( %flagFieldName ); + + %selected = %this.getSelection(); + foreach( %object in %selected ) + %object.setFieldValue( %flagFieldName, !%object.getFieldValue( %flagFieldName ) ); + + GuiEditorInspectFields.onInspectorPostFieldModification(); + GuiEditorInspectFields.refresh(); +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onDelete(%this) +{ + GuiEditorTreeView.update(); + // clear out the gui inspector. + GuiEditorInspectFields.update(0); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onSelectionMoved( %this, %ctrl ) +{ + GuiEditorInspectFields.update( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onSelectionResized( %this, %ctrl ) +{ + GuiEditorInspectFields.update( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onSelect(%this, %ctrl) +{ + if( !%this.dontSyncTreeViewSelection ) + { + GuiEditorTreeView.clearSelection(); + GuiEditorTreeView.addSelection( %ctrl ); + } + + GuiEditorInspectFields.update( %ctrl ); + + GuiEditorSelectionStatus.setText( "1 Control Selected" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onAddSelected( %this, %ctrl ) +{ + if( !%this.dontSyncTreeViewSelection ) + { + GuiEditorTreeView.addSelection( %ctrl ); + GuiEditorTreeView.scrollVisibleByObjectId( %ctrl ); + } + + GuiEditorSelectionStatus.setText( %this.getNumSelected() @ " Controls Selected" ); + + // Add to inspection set. + + GuiEditorInspectFields.addInspect( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onRemoveSelected( %this, %ctrl ) +{ + if( !%this.dontSyncTreeViewSelection ) + GuiEditorTreeView.removeSelection( %ctrl ); + + GuiEditorSelectionStatus.setText( %this.getNumSelected() @ " Controls Selected" ); + + // Remove from inspection set. + + GuiEditorInspectFields.removeInspect( %ctrl ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onClearSelected( %this ) +{ + if( !%this.dontSyncTreeViewSelection ) + GuiEditorTreeView.clearSelection(); + + GuiEditorInspectFields.update( 0 ); + GuiEditorSelectionStatus.setText( "" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onControlDragged( %this, %payload, %position ) +{ + // Make sure we have the right kind of D&D. + + if( !%payload.parentGroup.isInNamespaceHierarchy( "GuiDragAndDropControlType_GuiControl" ) ) + return; + + // use the position under the mouse cursor, not the payload position. + %position = VectorSub( %position, GuiEditorContent.getGlobalPosition() ); + %x = getWord( %position, 0 ); + %y = getWord( %position, 1 ); + %target = GuiEditor.getContentControl().findHitControl( %x, %y ); + + // Make sure the target is a valid parent for our payload. + + while( ( !%target.isContainer || !%target.acceptsAsChild( %payload ) ) + && %target != GuiEditor.getContentControl() ) + %target = %target.getParent(); + + if( %target != %this.getCurrentAddSet() ) + %this.setCurrentAddSet( %target ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onControlDropped(%this, %payload, %position) +{ + // Make sure we have the right kind of D&D. + + if( !%payload.parentGroup.isInNamespaceHierarchy( "GuiDragAndDropControlType_GuiControl" ) ) + return; + + %pos = %payload.getGlobalPosition(); + %x = getWord(%pos, 0); + %y = getWord(%pos, 1); + + %this.addNewCtrl(%payload); + + %payload.setPositionGlobal(%x, %y); + %this.setFirstResponder(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onGainFirstResponder(%this) +{ + %this.enableMenuItems(true); + + // JCF: don't just turn them all on! + // Undo/Redo is only enabled if those actions exist. + %this.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onLoseFirstResponder(%this) +{ + %this.enableMenuItems(false); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onHierarchyChanged( %this ) +{ + GuiEditorTreeView.update(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::onMouseModeChange( %this ) +{ + GuiEditorStatusBar.setText( GuiEditorStatusBar.getMouseModeHelp() ); +} + +//============================================================================================= +// Resolution List. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorResList::init( %this ) +{ + %this.clear(); + + // Non-widescreen formats. + + %this.add( "640x480 (VGA, 4:3)", 640 ); + %this.add( "800x600 (SVGA, 4:3)", 800 ); + %this.add( "1024x768 (XGA, 4:3)", 1024 ); + %this.add( "1280x1024 (SXGA, 4:3)", 1280 ); + %this.add( "1600x1200 (UXGA, 4:3)", 1600 ); + + // Widescreen formats. + + %this.add( "1280x720 (WXGA, 16:9)", 720 ); + %this.add( "1600x900 (16:9)", 900 ); + %this.add( "1920x1080 (16:9)", 1080 ); + %this.add( "1440x900 (WXGA+, 16:10)", 900 ); + %this.add( "1680x1050 (WSXGA+, 16:10)", 1050 ); + %this.add( "1920x1200 (WUXGA, 16:10)", 1200 ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorResList::selectFormat( %this, %format ) +{ + %width = getWord( %format, 0 ); + %height = getWord( %format, 1 ); + + switch( %height ) + { + case 720: + %this.setSelected( 720 ); + + case 900: + %this.setSelected( 900 ); + + case 1050: + %this.setSelected( 1050 ); + + case 1080: + %this.setSelected( 1080 ); + + default: + + switch( %width ) + { + case 640: + %this.setSelected( 640 ); + + case 800: + %this.setSelected( 800 ); + + case 1024: + %this.setSelected( 1024 ); + + case 1280: + %this.setSelected( 1280 ); + + case 1600: + %this.setSelected( 1600 ); + + default: + %this.setSelected( 1200 ); + } + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorResList::onSelect( %this, %id ) +{ + switch( %id ) + { + case 640: + GuiEditor.setPreviewResolution( 640, 480 ); + + case 800: + GuiEditor.setPreviewResolution( 800, 600 ); + + case 1024: + GuiEditor.setPreviewResolution( 1024, 768 ); + + case 1280: + GuiEditor.setPreviewResolution( 1280, 1024 ); + + case 1600: + GuiEditor.setPreviewResolution( 1600, 1200 ); + + case 720: + GuiEditor.setPreviewResolution( 1280, 720 ); + + case 900: + GuiEditor.setPreviewResolution( 1440, 900 ); + + case 1050: + GuiEditor.setPreviewResolution( 1680, 1050 ); + + case 1080: + GuiEditor.setPreviewResolution( 1920, 1080 ); + + case 1200: + GuiEditor.setPreviewResolution( 1920, 1200 ); + } +} + +//============================================================================================= +// Sidebar. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTabBook::onWake( %this ) +{ + if( !isObject( "GuiEditorTabBookLibraryPopup" ) ) + new PopupMenu( GuiEditorTabBookLibraryPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Alphabetical View" TAB "" TAB "GuiEditorToolbox.setViewType( \"Alphabetical\" );"; + item[ 1 ] = "Categorized View" TAB "" TAB "GuiEditorToolbox.setViewType( \"Categorized\" );"; + }; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTabBook::onTabSelected( %this, %text, %index ) +{ + %sidebar = GuiEditorSidebar; + %name = %this.getObject( %index ).getInternalName(); + + switch$( %name ) + { + case "guiPage": + + %sidebar-->button1.setVisible( false ); + %sidebar-->button2.setVisible( false ); + %sidebar-->button3.setVisible( true ); + %sidebar-->button4.setVisible( true ); + + %sidebar-->button4.setBitmap( "tools/gui/images/delete" ); + %sidebar-->button4.command = "GuiEditor.deleteSelection();"; + %sidebar-->button4.tooltip = "Delete Selected Control(s)"; + + %sidebar-->button3.setBitmap( "tools/gui/images/visible" ); + %sidebar-->button3.command = "GuiEditor.toggleHideSelection();"; + %sidebar-->button3.tooltip = "Hide Selected Control(s)"; + + case "profilesPage": + + %sidebar-->button1.setVisible( true ); + %sidebar-->button2.setVisible( true ); + %sidebar-->button3.setVisible( true ); + %sidebar-->button4.setVisible( true ); + + %sidebar-->button4.setBitmap( "tools/gui/images/delete" ); + %sidebar-->button4.command = "GuiEditor.showDeleteProfileDialog( GuiEditorProfilesTree.getSelectedProfile() );"; + %sidebar-->button4.tooltip = "Delete Selected Profile"; + + %sidebar-->button3.setBitmap( "tools/gui/images/new" ); + %sidebar-->button3.command = "GuiEditor.createNewProfile( \"Unnamed\" );"; + %sidebar-->button3.tooltip = "Create New Profile with Default Values"; + + %sidebar-->button2.setBitmap( "tools/gui/images/copy-btn" ); + %sidebar-->button2.command = "GuiEditor.createNewProfile( GuiEditorProfilesTree.getSelectedProfile().getName(), GuiEditorProfilesTree.getSelectedProfile() );"; + %sidebar-->button2.tooltip = "Create New Profile by Copying the Selected Profile"; + + %sidebar-->button1.setBitmap( "tools/gui/images/reset-icon" ); + %sidebar-->button1.command = "GuiEditor.revertProfile( GuiEditorProfilesTree.getSelectedProfile() );"; + %sidebar-->button1.tooltip = "Revert Changes to the Selected Profile"; + + case "toolboxPage": + + //TODO + + %sidebar-->button1.setVisible( false ); + %sidebar-->button2.setVisible( false ); + %sidebar-->button3.setVisible( false ); + %sidebar-->button4.setVisible( false ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTabBook::onTabRightClick( %this, %text, %index ) +{ + %name = %this.getObject( %index ).getInternalName(); + + switch$( %name ) + { + case "toolboxPage": + + // Open toolbox popup. + + %popup = GuiEditorTabBookLibraryPopup; + + %currentViewType = GuiEditorToolbox.getViewType(); + switch$( %currentViewType ) + { + case "Alphabetical": + %popup.checkRadioItem( 0, 1, 0 ); + + case "Categorized": + %popup.checkRadioItem( 0, 1, 1 ); + } + + %popup.showPopup( Canvas ); + } +} + +//============================================================================================= +// Toolbar. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSnapCheckBox::onWake(%this) +{ + %snap = GuiEditor.snap2grid * GuiEditor.snap2gridsize; + %this.setValue( %snap ); + GuiEditor.setSnapToGrid( %snap ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSnapCheckBox::onAction(%this) +{ + %snap = GuiEditor.snap2gridsize * %this.getValue(); + GuiEditor.snap2grid = %this.getValue(); + GuiEditor.setSnapToGrid(%snap); +} + +//============================================================================================= +// GuiEditorGui. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorGui::onWake( %this ) +{ + GHGuiEditor.setStateOn( 1 ); + + if( !isObject( %this->SelectControlsDlg ) ) + { + %this.add( GuiEditorSelectDlg ); + GuiEditorSelectDlg.setVisible( false ); + } + + // Attach our menus. + + if( isObject( %this.menuGroup ) ) + for( %i = 0; %i < %this.menuGroup.getCount(); %i ++ ) + %this.menuGroup.getObject( %i ).attachToMenuBar(); + + // Read settings. + + %this.initSettings(); + %this.readSettings(); + + // Initialize toolbox. + + if( !GuiEditorToolbox.isInitialized ) + GuiEditorToolbox.initialize(); + + // Set up initial menu toggle states. + + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_EDGESNAP_INDEX, GuiEditor.snapToEdges ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CENTERSNAP_INDEX, GuiEditor.snapToCenters ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_GUIDESNAP_INDEX, GuiEditor.snapToGuides ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CONTROLSNAP_INDEX, GuiEditor.snapToControls ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_CANVASSNAP_INDEX, GuiEditor.snapToCanvas ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_GRIDSNAP_INDEX, GuiEditor.snap2Grid ); + GuiEditCanvas.menuBar->SnapMenu.checkItem( $GUI_EDITOR_MENU_DRAWGUIDES_INDEX, GuiEditor.drawGuides ); + GuiEditCanvas.menuBar->EditMenu.checkItem( $GUI_EDITOR_MENU_FULLBOXSELECT_INDEX, GuiEditor.fullBoxSelection ); + + // Sync toolbar buttons. + + GuiEditorSnapCheckBox.setStateOn( GuiEditor.snap2Grid ); + GuiEditorEdgeSnapping_btn.setStateOn( GuiEditor.snapToEdges ); + GuiEditorCenterSnapping_btn.setStateOn( GuiEditor.snapToCenters ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorGui::onSleep( %this) +{ + // If we are editing a control, store its guide state. + + %content = GuiEditor.getContentControl(); + if( isObject( %content ) ) + GuiEditor.writeGuides( %content ); + + // Remove our menus. + + if( isObject( %this.menuGroup ) ) + for( %i = 0; %i < %this.menuGroup.getCount(); %i ++ ) + %this.menuGroup.getObject( %i ).removeFromMenuBar(); + + // Store our preferences. + + %this.writeSettings(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorGui::initSettings( %this ) +{ + EditorSettings.beginGroup( "GuiEditor", true ); + + EditorSettings.setDefaultValue( "lastPath", "" ); + EditorSettings.setDefaultValue( "previewResolution", "1024 768" ); + + EditorSettings.beginGroup( "EngineDevelopment" ); + EditorSettings.setDefaultValue( "toggleIntoEditor", 0 ); + EditorSettings.setDefaultValue( "showEditorProfiles", 0 ); + EditorSettings.setDefaultValue( "showEditorGuis", 0 ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Library" ); + EditorSettings.setDefaultValue( "viewType", "Categorized" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Snapping" ); + EditorSettings.setDefaultValue( "snapToControls", "1" ); + EditorSettings.setDefaultValue( "snapToGuides", "1" ); + EditorSettings.setDefaultValue( "snapToCanvas", "1" ); + EditorSettings.setDefaultValue( "snapToEdges", "1" ); + EditorSettings.setDefaultValue( "snapToCenters", "1" ); + EditorSettings.setDefaultValue( "sensitivity", "2" ); + EditorSettings.setDefaultValue( "snap2Grid", "0" ); + EditorSettings.setDefaultValue( "snap2GridSize", $GuiEditor::defaultGridSize ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Selection" ); + EditorSettings.setDefaultValue( "fullBox", "0" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Rendering" ); + EditorSettings.setDefaultValue( "drawBorderLines", "1" ); + EditorSettings.setDefaultValue( "drawGuides", "1" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Help" ); + EditorSettings.setDefaultValue( "documentationURL", "http://www.garagegames.com/products/torque-3d/documentation/user" ); //RDTODO: make this point to Gui Editor docs when available + + // Create a path to the local documentation. This is a bit of guesswork here. + // It assumes that the project is located in a folder of the SDK root directory + // (e.g. "Examples/" or "Demos/") and that from there the path to the game + // folder is "<project>/game". + EditorSettings.setDefaultValue("documentationLocal", "../../../Documentation/Official Documentation.html" ); + + EditorSettings.setDefaultValue("documentationReference", "../../../Documentation/Torque 3D - Script Manual.chm" ); + + EditorSettings.endGroup(); + + EditorSettings.endGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorGui::readSettings( %this ) +{ + EditorSettings.read(); + + EditorSettings.beginGroup( "GuiEditor", true ); + + GuiEditor.lastPath = EditorSettings.value( "lastPath" ); + GuiEditor.previewResolution = EditorSettings.value( "previewResolution" ); + + EditorSettings.beginGroup( "EngineDevelopment" ); + GuiEditor.toggleIntoEditor = EditorSettings.value( "toggleIntoEditor" ); + GuiEditor.showEditorProfiles = EditorSettings.value( "showEditorProfiles" ); + GuiEditor.showEditorGuis = EditorSettings.value( "showEditorGuis" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Library" ); + GuiEditorToolbox.currentViewType = EditorSettings.value( "viewType" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Snapping" ); + GuiEditor.snapToGuides = EditorSettings.value( "snapToGuides" ); + GuiEditor.snapToControls = EditorSettings.value( "snapToControls" ); + GuiEditor.snapToCanvas = EditorSettings.value( "snapToCanvas" ); + GuiEditor.snapToEdges = EditorSettings.value( "snapToEdges" ); + GuiEditor.snapToCenters = EditorSettings.value( "snapToCenters" ); + GuiEditor.snapSensitivity = EditorSettings.value( "sensitivity" ); + GuiEditor.snap2Grid = EditorSettings.value( "snap2Grid" ); + GuiEditor.snap2GridSize = EditorSettings.value( "snap2GridSize" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Selection" ); + GuiEditor.fullBoxSelection = EditorSettings.value( "fullBox" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Rendering" ); + GuiEditor.drawBorderLines = EditorSettings.value( "drawBorderLines" ); + GuiEditor.drawGuides = EditorSettings.value( "drawGuides" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Help" ); + GuiEditor.documentationURL = EditorSettings.value( "documentationURL" ); + GuiEditor.documentationLocal = EditorSettings.value( "documentationLocal" ); + GuiEditor.documentationReference = EditorSettings.value( "documentationReference" ); + EditorSettings.endGroup(); + + EditorSettings.endGroup(); + + if( GuiEditor.snap2Grid ) + GuiEditor.setSnapToGrid( GuiEditor.snap2GridSize ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorGui::writeSettings( %this ) +{ + EditorSettings.beginGroup( "GuiEditor", true ); + + EditorSettings.setValue( "lastPath", GuiEditor.lastPath ); + EditorSettings.setValue( "previewResolution", GuiEditor.previewResolution ); + + EditorSettings.beginGroup( "EngineDevelopment" ); + EditorSettings.setValue( "toggleIntoEditor", GuiEditor.toggleIntoEditor ); + EditorSettings.setValue( "showEditorProfiles", GuiEditor.showEditorProfiles ); + EditorSettings.setValue( "showEditorGuis", GuiEditor.showEditorGuis ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Library" ); + EditorSettings.setValue( "viewType", GuiEditorToolbox.currentViewType ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Snapping" ); + EditorSettings.setValue( "snapToControls", GuiEditor.snapToControls ); + EditorSettings.setValue( "snapToGuides", GuiEditor.snapToGuides ); + EditorSettings.setValue( "snapToCanvas", GuiEditor.snapToCanvas ); + EditorSettings.setValue( "snapToEdges", GuiEditor.snapToEdges ); + EditorSettings.setValue( "snapToCenters", GuiEditor.snapToCenters ); + EditorSettings.setValue( "sensitivity", GuiEditor.snapSensitivity ); + EditorSettings.setValue( "snap2Grid", GuiEditor.snap2Grid ); + EditorSettings.setValue( "snap2GridSize", GuiEditor.snap2GridSize ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Selection" ); + EditorSettings.setValue( "fullBox", GuiEditor.fullBoxSelection ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Rendering" ); + EditorSettings.setValue( "drawBorderLines", GuiEditor.drawBorderLines ); + EditorSettings.setValue( "drawGuides", GuiEditor.drawGuides ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Help" ); + EditorSettings.setValue( "documentationURL", GuiEditor.documentationURL ); + EditorSettings.setValue( "documentationLocal", GuiEditor.documentationLocal ); + EditorSettings.setValue( "documentationReference", GuiEditor.documentationReference ); + EditorSettings.endGroup(); + + EditorSettings.endGroup(); + + EditorSettings.write(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorCanvas.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorCanvas.ed.cs new file mode 100644 index 000000000..1305ae170 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorCanvas.ed.cs @@ -0,0 +1,540 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::onAdd( %this ) +{ + // %this.setWindowTitle("Torque Gui Editor"); + + %this.onCreateMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::onRemove( %this ) +{ + if( isObject( GuiEditorGui.menuGroup ) ) + GuiEditorGui.delete(); + + // cleanup + %this.onDestroyMenu(); +} + +//--------------------------------------------------------------------------------------------- + +/// Create the Gui Editor menu bar. +function GuiEditCanvas::onCreateMenu(%this) +{ + if(isObject(%this.menuBar)) + return; + + //set up %cmdctrl variable so that it matches OS standards + if( $platform $= "macos" ) + { + %cmdCtrl = "cmd"; + %redoShortcut = "Cmd-Shift Z"; + } + else + { + %cmdCtrl = "Ctrl"; + %redoShort = "Ctrl Y"; + } + + // Menu bar + %this.menuBar = new MenuBar() + { + dynamicItemInsertPos = 3; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "File"; + internalName = "FileMenu"; + + item[0] = "New Gui..." TAB %cmdCtrl SPC "N" TAB %this @ ".create();"; + item[1] = "Open..." TAB %cmdCtrl SPC "O" TAB %this @ ".open();"; + item[2] = "Save" TAB %cmdCtrl SPC "S" TAB %this @ ".save( false, true );"; + item[3] = "Save As..." TAB %cmdCtrl @ "-Shift S" TAB %this @ ".save( false );"; + item[4] = "Save Selected As..." TAB %cmdCtrl @ "-Alt S" TAB %this @ ".save( true );"; + item[5] = "-"; + item[6] = "Revert Gui" TAB "" TAB %this @ ".revert();"; + item[7] = "Add Gui From File..." TAB "" TAB %this @ ".append();"; + item[8] = "-"; + item[9] = "Open Gui File in Torsion" TAB "" TAB %this @".openInTorsion();"; + item[10] = "-"; + item[11] = "Close Editor" TAB "F10" TAB %this @ ".quit();"; + item[12] = "Quit" TAB %cmdCtrl SPC "Q" TAB "quit();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "Edit"; + internalName = "EditMenu"; + + item[0] = "Undo" TAB %cmdCtrl SPC "Z" TAB "GuiEditor.undo();"; + item[1] = "Redo" TAB %redoShortcut TAB "GuiEditor.redo();"; + item[2] = "-"; + item[3] = "Cut" TAB %cmdCtrl SPC "X" TAB "GuiEditor.saveSelection(); GuiEditor.deleteSelection();"; + item[4] = "Copy" TAB %cmdCtrl SPC "C" TAB "GuiEditor.saveSelection();"; + item[5] = "Paste" TAB %cmdCtrl SPC "V" TAB "GuiEditor.loadSelection();"; + item[6] = "-"; + item[7] = "Select All" TAB %cmdCtrl SPC "A" TAB "GuiEditor.selectAll();"; + item[8] = "Deselect All" TAB %cmdCtrl SPC "D" TAB "GuiEditor.clearSelection();"; + item[9] = "Select Parent(s)" TAB %cmdCtrl @ "-Alt Up" TAB "GuiEditor.selectParents();"; + item[10] = "Select Children" TAB %cmdCtrl @ "-Alt Down" TAB "GuiEditor.selectChildren();"; + item[11] = "Add Parent(s) to Selection" TAB %cmdCtrl @ "-Alt-Shift Up" TAB "GuiEditor.selectParents( true );"; + item[12] = "Add Children to Selection" TAB %cmdCtrl @ "-Alt-Shift Down" TAB "GuiEditor.selectChildren( true );"; + item[13] = "Select..." TAB "" TAB "GuiEditorSelectDlg.toggleVisibility();"; + item[14] = "-"; + item[15] = "Lock/Unlock Selection" TAB %cmdCtrl SPC "L" TAB "GuiEditor.toggleLockSelection();"; + item[16] = "Hide/Unhide Selection" TAB %cmdCtrl SPC "H" TAB "GuiEditor.toggleHideSelection();"; + item[17] = "-"; + item[18] = "Group Selection" TAB %cmdCtrl SPC "G" TAB "GuiEditor.groupSelected();"; + item[19] = "Ungroup Selection" TAB %cmdCtrl @ "-Shift G" TAB "GuiEditor.ungroupSelected();"; + item[20] = "-"; + item[21] = "Full Box Selection" TAB "" TAB "GuiEditor.toggleFullBoxSelection();"; + item[22] = "-"; + item[23] = "Grid Size" TAB %cmdCtrl SPC "," TAB "GuiEditor.showPrefsDialog();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "Layout"; + internalName = "LayoutMenu"; + + item[0] = "Align Left" TAB %cmdCtrl SPC "Left" TAB "GuiEditor.Justify(0);"; + item[1] = "Center Horizontally" TAB "" TAB "GuiEditor.Justify(1);"; + item[2] = "Align Right" TAB %cmdCtrl SPC "Right" TAB "GuiEditor.Justify(2);"; + item[3] = "-"; + item[4] = "Align Top" TAB %cmdCtrl SPC "Up" TAB "GuiEditor.Justify(3);"; + item[5] = "Center Vertically" TAB "" TAB "GuiEditor.Justify(7);"; + item[6] = "Align Bottom" TAB %cmdCtrl SPC "Down" TAB "GuiEditor.Justify(4);"; + item[7] = "-"; + item[8] = "Space Vertically" TAB "" TAB "GuiEditor.Justify(5);"; + item[9] = "Space Horizontally" TAB "" TAB "GuiEditor.Justify(6);"; + item[10] = "-"; + item[11] = "Fit into Parent(s)" TAB "" TAB "GuiEditor.fitIntoParents();"; + item[12] = "Fit Width to Parent(s)" TAB "" TAB "GuiEditor.fitIntoParents( true, false );"; + item[13] = "Fit Height to Parent(s)" TAB "" TAB "GuiEditor.fitIntoParents( false, true );"; + item[14] = "-"; + item[15] = "Bring to Front" TAB "" TAB "GuiEditor.BringToFront();"; + item[16] = "Send to Back" TAB "" TAB "GuiEditor.PushToBack();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "Move"; + internalName = "MoveMenu"; + + item[0] = "Nudge Left" TAB "Left" TAB "GuiEditor.moveSelection( -1, 0);"; + item[1] = "Nudge Right" TAB "Right" TAB "GuiEditor.moveSelection( 1, 0);"; + item[2] = "Nudge Up" TAB "Up" TAB "GuiEditor.moveSelection( 0, -1);"; + item[3] = "Nudge Down" TAB "Down" TAB "GuiEditor.moveSelection( 0, 1 );"; + item[4] = "-"; + item[5] = "Big Nudge Left" TAB "Shift Left" TAB "GuiEditor.moveSelection( - GuiEditor.snap2gridsize, 0 );"; + item[6] = "Big Nudge Right" TAB "Shift Right" TAB "GuiEditor.moveSelection( GuiEditor.snap2gridsize, 0 );"; + item[7] = "Big Nudge Up" TAB "Shift Up" TAB "GuiEditor.moveSelection( 0, - GuiEditor.snap2gridsize );"; + item[8] = "Big Nudge Down" TAB "Shift Down" TAB "GuiEditor.moveSelection( 0, GuiEditor.snap2gridsize );"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + barTitle = "Snap"; + internalName = "SnapMenu"; + + item[0] = "Snap Edges" TAB "Alt-Shift E" TAB "GuiEditor.toggleEdgeSnap();"; + item[1] = "Snap Centers" TAB "Alt-Shift C" TAB "GuiEditor.toggleCenterSnap();"; + item[2] = "-"; + item[3] = "Snap to Guides" TAB "Alt-Shift G" TAB "GuiEditor.toggleGuideSnap();"; + item[4] = "Snap to Controls" TAB "Alt-Shift T" TAB "GuiEditor.toggleControlSnap();"; + item[5] = "Snap to Canvas" TAB "" TAB "GuiEditor.toggleCanvasSnap();"; + item[6] = "Snap to Grid" TAB "" TAB "GuiEditor.toggleGridSnap();"; + item[7] = "-"; + item[8] = "Show Guides" TAB "" TAB "GuiEditor.toggleDrawGuides();"; + item[9] = "Clear Guides" TAB "" TAB "GuiEditor.clearGuides();"; + }; + + new PopupMenu() + { + superClass = "MenuBuilder"; + internalName = "HelpMenu"; + + barTitle = "Help"; + + item[0] = "Online Documentation..." TAB "Alt F1" TAB "gotoWebPage( GuiEditor.documentationURL );"; + item[1] = "Offline User Guid..." TAB "" TAB "gotoWebPage( GuiEditor.documentationLocal );"; + item[2] = "Offline Reference Guide..." TAB "" TAB "shellExecute( GuiEditor.documentationReference );"; + item[3] = "-"; + item[4] = "Torque 3D Public Forums..." TAB "" TAB "gotoWebPage( \"http://www.garagegames.com/community/forums/73\" );"; + item[5] = "Torque 3D Private Forums..." TAB "" TAB "gotoWebPage( \"http://www.garagegames.com/community/forums/63\" );"; + }; + }; + %this.menuBar.attachToCanvas( Canvas, 0 ); +} + +$GUI_EDITOR_MENU_EDGESNAP_INDEX = 0; +$GUI_EDITOR_MENU_CENTERSNAP_INDEX = 1; +$GUI_EDITOR_MENU_GUIDESNAP_INDEX = 3; +$GUI_EDITOR_MENU_CONTROLSNAP_INDEX = 4; +$GUI_EDITOR_MENU_CANVASSNAP_INDEX = 5; +$GUI_EDITOR_MENU_GRIDSNAP_INDEX = 6; +$GUI_EDITOR_MENU_DRAWGUIDES_INDEX = 8; +$GUI_EDITOR_MENU_FULLBOXSELECT_INDEX = 21; + +//--------------------------------------------------------------------------------------------- + +/// Called before onSleep when the canvas content is changed +function GuiEditCanvas::onDestroyMenu(%this) +{ + if( !isObject( %this.menuBar ) ) + return; + + // Destroy menus + while( %this.menuBar.getCount() != 0 ) + %this.menuBar.getObject( 0 ).delete(); + + %this.menuBar.removeFromCanvas(); + %this.menuBar.delete(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::onWindowClose(%this) +{ + %this.quit(); +} + +//============================================================================================= +// Menu Commands. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::create( %this ) +{ + GuiEditorNewGuiDialog.init( "NewGui", "GuiControl" ); + + Canvas.pushDialog( GuiEditorNewGuiDialog ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::load( %this, %filename ) +{ + %newRedefineBehavior = "replaceExisting"; + if( isDefined( "$GuiEditor::loadRedefineBehavior" ) ) + { + // This trick allows to choose different redefineBehaviors when loading + // GUIs. This is useful, for example, when loading GUIs that would lead to + // problems when loading with their correct names because script behavior + // would immediately attach. + // + // This allows to also edit the GUI editor's own GUI inside itself. + + %newRedefineBehavior = $GuiEditor::loadRedefineBehavior; + } + + // Allow stomping objects while exec'ing the GUI file as we want to + // pull the file's objects even if we have another version of the GUI + // already loaded. + + %oldRedefineBehavior = $Con::redefineBehavior; + $Con::redefineBehavior = %newRedefineBehavior; + + // Load up the gui. + exec( %fileName ); + + $Con::redefineBehavior = %oldRedefineBehavior; + + // The GUI file should have contained a GUIControl which should now be in the instant + // group. And, it should be the only thing in the group. + if( !isObject( %guiContent ) ) + { + MessageBox( getEngineName(), + "You have loaded a Gui file that was created before this version. It has been loaded but you must open it manually from the content list dropdown", + "Ok", "Information" ); + return 0; + } + + GuiEditor.openForEditing( %guiContent ); + + GuiEditorStatusBar.print( "Loaded '" @ %filename @ "'" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::openInTorsion( %this ) +{ + if( !GuiEditorContent.getCount() ) + return; + + %guiObject = GuiEditorContent.getObject( 0 ); + EditorOpenDeclarationInTorsion( %guiObject ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::open( %this ) +{ + %openFileName = GuiBuilder::getOpenName(); + if( %openFileName $= "" ) + return; + + // Make sure the file is valid. + if ((!isFile(%openFileName)) && (!isFile(%openFileName @ ".dso"))) + return; + + %this.load( %openFileName ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::save( %this, %selectedOnly, %noPrompt ) +{ + // Get the control we should save. + + if( %selectedOnly ) + { + %selected = GuiEditor.getSelection(); + if( !%selected.getCount() ) + return; + else if( %selected.getCount() > 1 ) + { + MessageBox( "Invalid selection", "Only a single control hierarchy can be saved to a file. Make sure you have selected only one control in the tree view." ); + return; + } + + %currentObject = %selected.getObject( 0 ); + } + else if( GuiEditorContent.getCount() > 0 ) + %currentObject = GuiEditorContent.getObject( 0 ); + else + return; + + // Store the current guide set on the control. + + GuiEditor.writeGuides( %currentObject ); + %currentObject.canSaveDynamicFields = true; // Make sure the guides get saved out. + + // Construct a base filename. + + if( %currentObject.getName() !$= "" ) + %name = %currentObject.getName() @ ".gui"; + else + %name = "Untitled.gui"; + + // Construct a path. + + if( %selectedOnly + && %currentObject != GuiEditorContent.getObject( 0 ) + && %currentObject.getFileName() $= GuiEditorContent.getObject( 0 ).getFileName() ) + { + // Selected child control that hasn't been yet saved to its own file. + + %currentFile = GuiEditor.LastPath @ "/" @ %name; + %currentFile = makeRelativePath( %currentFile, getMainDotCsDir() ); + } + else + { + %currentFile = %currentObject.getFileName(); + if( %currentFile $= "") + { + // No file name set on control. Force a prompt. + %noPrompt = false; + + if( GuiEditor.LastPath !$= "" ) + { + %currentFile = GuiEditor.LastPath @ "/" @ %name; + %currentFile = makeRelativePath( %currentFile, getMainDotCsDir() ); + } + else + %currentFile = expandFileName( %name ); + } + else + %currentFile = expandFileName( %currentFile ); + } + + // Get the filename. + + if( !%noPrompt ) + { + %filename = GuiBuilder::getSaveName( %currentFile ); + if( %filename $= "" ) + return; + } + else + %filename = %currentFile; + + // Save the Gui. + + if( isWriteableFileName( %filename ) ) + { + // + // Extract any existent TorqueScript before writing out to disk + // + %fileObject = new FileObject(); + %fileObject.openForRead( %filename ); + %skipLines = true; + %beforeObject = true; + // %var++ does not post-increment %var, in torquescript, it pre-increments it, + // because ++%var is illegal. + %lines = -1; + %beforeLines = -1; + %skipLines = false; + while( !%fileObject.isEOF() ) + { + %line = %fileObject.readLine(); + if( %line $= "//--- OBJECT WRITE BEGIN ---" ) + %skipLines = true; + else if( %line $= "//--- OBJECT WRITE END ---" ) + { + %skipLines = false; + %beforeObject = false; + } + else if( %skipLines == false ) + { + if(%beforeObject) + %beforeNewFileLines[ %beforeLines++ ] = %line; + else + %newFileLines[ %lines++ ] = %line; + } + } + %fileObject.close(); + %fileObject.delete(); + + %fo = new FileObject(); + %fo.openForWrite(%filename); + + // Write out the captured TorqueScript that was before the object before the object + for( %i = 0; %i <= %beforeLines; %i++) + %fo.writeLine( %beforeNewFileLines[ %i ] ); + + %fo.writeLine("//--- OBJECT WRITE BEGIN ---"); + %fo.writeObject(%currentObject, "%guiContent = "); + %fo.writeLine("//--- OBJECT WRITE END ---"); + + // Write out captured TorqueScript below Gui object + for( %i = 0; %i <= %lines; %i++ ) + %fo.writeLine( %newFileLines[ %i ] ); + + %fo.close(); + %fo.delete(); + + %currentObject.setFileName( makeRelativePath( %filename, getMainDotCsDir() ) ); + + GuiEditorStatusBar.print( "Saved file '" @ %currentObject.getFileName() @ "'" ); + } + else + MessageBox( "Error writing to file", "There was an error writing to file '" @ %currentFile @ "'. The file may be read-only.", "Ok", "Error" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::append( %this ) +{ + // Get filename. + + %openFileName = GuiBuilder::getOpenName(); + if( %openFileName $= "" + || ( !isFile( %openFileName ) + && !isFile( %openFileName @ ".dso" ) ) ) + return; + + // Exec file. + + %oldRedefineBehavior = $Con::redefineBehavior; + $Con::redefineBehavior = "renameNew"; + exec( %openFileName ); + $Con::redefineBehavior = %oldRedefineBehavior; + + // Find guiContent. + + if( !isObject( %guiContent ) ) + { + MessageBox( "Error loading GUI file", "The GUI content controls could not be found. This function can only be used with files saved by the GUI editor.", "Ok", "Error" ); + return; + } + + if( !GuiEditorContent.getCount() ) + GuiEditor.openForEditing( %guiContent ); + else + { + GuiEditor.getCurrentAddSet().add( %guiContent ); + GuiEditor.readGuides( %guiContent ); + GuiEditor.onAddNewCtrl( %guiContent ); + GuiEditor.onHierarchyChanged(); + } + + GuiEditorStatusBar.print( "Appended controls from '" @ %openFileName @ "'" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::revert( %this ) +{ + if( !GuiEditorContent.getCount() ) + return; + + %gui = GuiEditorContent.getObject( 0 ); + %filename = %gui.getFileName(); + if( %filename $= "" ) + return; + + if( MessageBox( "Revert Gui", "Really revert the current Gui? This cannot be undone.", "OkCancel", "Question" ) == $MROk ) + %this.load( %filename ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::close( %this ) +{ +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditCanvas::quit( %this ) +{ + %this.close(); + GuiGroup.add(GuiEditorGui); + // we must not delete a window while in its event handler, or we foul the event dispatch mechanism + %this.schedule(10, delete); + + Canvas.setContent(GuiEditor.lastContent); + $InGuiEditor = false; + + //Temp fix to disable MLAA when in GUI editor + if( isObject(MLAAFx) && $MLAAFxGuiEditorTemp==true ) + { + MLAAFx.isEnabled = true; + $MLAAFxGuiEditorTemp = false; + } +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorContentList.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorContentList.ed.cs new file mode 100644 index 000000000..8a946a2a2 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorContentList.ed.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Code for the drop-down that allows selecting a GUI to edit in the Gui Editor. + + +if( !isDefined( "$GuiEditor::GuiFilterList" ) ) +{ + /// List of named controls that are filtered out from the + /// control list dropdown. + $GuiEditor::GuiFilterList = + "GuiEditorGui" TAB + "AL_ShadowVizOverlayCtrl" TAB + "MessageBoxOKDlg" TAB + "MessageBoxOKCancelDlg" TAB + "MessageBoxOKCancelDetailsDlg" TAB + "MessageBoxYesNoDlg" TAB + "MessageBoxYesNoCancelDlg" TAB + "MessagePopupDlg"; +} + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorContentList::init( %this ) +{ + %this.clear(); + %this.scanGroup( GuiGroup ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorContentList::scanGroup( %this, %group ) +{ + foreach( %obj in %group ) + { + if( %obj.isMemberOfClass( "GuiControl" ) ) + { + if(%obj.getClassName() $= "GuiCanvas") + { + %this.scanGroup( %obj ); + } + else + { + if(%obj.getName() $= "") + %name = "(unnamed) - " @ %obj; + else + %name = %obj.getName() @ " - " @ %obj; + + %skip = false; + + foreach$( %guiEntry in $GuiEditor::GuiFilterList ) + if( %obj.getName() $= %guiEntry ) + { + %skip = true; + break; + } + + if( !%skip ) + %this.add( %name, %obj ); + } + } + else if( %obj.isMemberOfClass( "SimGroup" ) + && ( %obj.internalName !$= "EditorGuiGroup" // Don't put our editor's GUIs in the list + || GuiEditor.showEditorGuis ) ) // except if explicitly requested. + { + // Scan nested SimGroups for GuiControls. + + %this.scanGroup( %obj ); + } + } +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorContentList::onSelect( %this, %ctrl ) +{ + GuiEditor.openForEditing( %ctrl ); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorGroup.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorGroup.ed.cs new file mode 100644 index 000000000..558aa7763 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorGroup.ed.cs @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// GuiEditorGroups are recorded only for undo/redo. They are ScriptObjects +// containing all the information about a particular GUIControl group. +// +// The properties of GuiEditorGroups are: +// +// int count - Number of controls in group. +// object ctrl[ 0 .. count ] - Controls in group. +// object ctrlParent[ 0 .. count ] - Original parent of each control in group (if missing, controls are moved to parent of group object in ungroup()) +// object groupObject - The GuiControl group object. +// object groupParent - The object to which the group should be parented to. + + +/// Return the combined global bounds of the controls contained in the GuiEditorGroup. +function GuiEditorGroup::getGlobalBounds( %this ) +{ + %minX = 2147483647; + %minY = 2147483647; + %maxX = -2147483647; + %maxY = -2147483647; + + for( %i = 0; %i < %this.count; %i ++ ) + { + %ctrl = %this.ctrl[ %i ]; + + %pos = %ctrl.getGlobalPosition(); + %extent = %ctrl.getExtent(); + + // Min. + + %posX = getWord( %pos, 0 ); + %posY = getWord( %pos, 1 ); + + if( %posX < %minX ) + %minX = %posX; + if( %posY < %minY ) + %minY = %posY; + + // Max. + + %posX += getWord( %extent, 0 ); + %posY += getWord( %extent, 1 ); + + if( %posX > %maxX ) + %maxX = %posX; + if( %posY > %maxY ) + %maxY = %posY; + } + + return ( %minX SPC %minY SPC ( %maxX - %minX ) SPC ( %maxY - %minY ) ); +} + +/// Create a new GuiControl and move all the controls contained in the GuiEditorGroup into it. +function GuiEditorGroup::group( %this ) +{ + %parent = %this.groupParent; + + // Create group. + + %group = new GuiControl(); + %parent.addGuiControl( %group ); + %this.groupObject = %group; + + // Make group fit around selection. + + %bounds = %this.getGlobalBounds(); + %parentGlobalPos = %parent.getGlobalPosition(); + + %x = getWord( %bounds, 0 ) - getWord( %parentGlobalPos, 0 ); + %y = getWord( %bounds, 1 ) - getWord( %parentGlobalPos, 1 ); + + %group.setPosition( %x, %y ); + %group.setExtent( getWord( %bounds, 2 ), getWord( %bounds, 3 ) ); + + // Reparent all objects to group. + + for( %i = 0; %i < %this.count; %i ++ ) + { + %ctrl = %this.ctrl[ %i ]; + + // Save parent for undo. + + %this.ctrlParent[ %i ] = %ctrl.parentGroup; + + // Reparent. + + %group.addGuiControl( %ctrl ); + + // Move into place in new parent. + + %pos = %ctrl.getPosition(); + %ctrl.setPosition( getWord( %pos, 0 ) - %x, getWord( %pos, 1 ) - %y ); + } +} + +/// Move all controls out of group to either former parent or group parent. +function GuiEditorGroup::ungroup( %this ) +{ + %defaultParent = %this.groupParent; + %groupPos = %this.groupObject.getPosition(); + + %x = getWord( %groupPos, 0 ); + %y = getWord( %groupPos, 1 ); + + // Move each control to its former parent (or default parent when + // there is no former parent). + + for( %i = 0; %i < %this.count; %i ++ ) + { + %ctrl = %this.ctrl[ %i ]; + + %parent = %defaultParent; + if( isObject( %this.ctrlParent[ %i ] ) ) + %parent = %this.ctrlParent[ %i ]; + + %parent.addGuiControl( %ctrl ); + + // Move into place in new parent. + + %ctrlPos = %ctrl.getPosition(); + %ctrl.setPosition( getWord( %ctrlPos, 0 ) + %x, getWord( %ctrlPos, 1 ) + %y ); + } + + // Delete old group object. + + %this.groupObject.delete(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorInspector.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorInspector.ed.cs new file mode 100644 index 000000000..fafc0a7c2 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorInspector.ed.cs @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Core for the main Gui Editor inspector that shows the properties of +// the currently selected control. + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::update( %this, %inspectTarget ) +{ + %this.inspect( %inspectTarget ); +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %oldValue; + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + // Restore the instant group. + popInstantGroup(); + + %action.addToManager( GuiEditor.getUndoManager() ); + + GuiEditor.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onInspectorPreFieldModification( %this, %fieldName, %arrayIndex ) +{ + pushInstantGroup(); + %undoManager = GuiEditor.getUndoManager(); + + %numObjects = %this.getNumInspectObjects(); + if( %numObjects > 1 ) + %action = %undoManager.pushCompound( "Multiple Field Edit" ); + + for( %i = 0; %i < %numObjects; %i ++ ) + { + %object = %this.getInspectObject( %i ); + + %nameOrClass = %object.getName(); + if( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %undo = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %object.getFieldValue( %fieldName, %arrayIndex ); + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + if( %numObjects > 1 ) + %undo.addToManager( %undoManager ); + else + { + %action = %undo; + break; + } + } + + %this.currentFieldEditAction = %action; + popInstantGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onInspectorPostFieldModification( %this ) +{ + if( %this.currentFieldEditAction.isMemberOfClass( "CompoundUndoAction" ) ) + { + // Finish multiple field edit. + GuiEditor.getUndoManager().popCompound(); + } + else + { + // Queue single field undo. + %this.currentFieldEditAction.addToManager( GuiEditor.getUndoManager() ); + } + + %this.currentFieldEditAction = ""; + GuiEditor.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onInspectorDiscardFieldModification( %this ) +{ + %this.currentFieldEditAction.undo(); + + if( %this.currentFieldEditAction.isMemberOfClass( "CompoundUndoAction" ) ) + { + // Multiple field editor. Pop and discard. + GuiEditor.getUndoManager().popCompound( true ); + } + else + { + // Single field edit. Just kill undo action. + %this.currentFieldEditAction.delete(); + } + + %this.currentFieldEditAction = ""; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + GuiEditorFieldInfo.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onBeginCompoundEdit( %this ) +{ + GuiEditor.getUndoManager().pushCompound( "Multiple Field Edits" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorInspectFields::onEndCompoundEdit( %this ) +{ + GuiEditor.getUndoManager().popCompound(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorNewGuiDialog.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorNewGuiDialog.ed.cs new file mode 100644 index 000000000..248c0e97d --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorNewGuiDialog.ed.cs @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Dialog for creating new GUIs. Allows to enter an object name and +// select a GuiControl class to use for the toplevel object. + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorNewGuiDialog::init( %this, %guiName, %guiClass ) +{ + %this-->nameField.setValue( %guiName ); + + // Initialize the class dropdown if we haven't already. + + %classDropdown = %this-->classDropdown; + if( !%classDropdown.size() ) + { + %classes = enumerateConsoleClassesByCategory( "Gui" ); + %count = getFieldCount( %classes ); + + for( %i = 0; %i < %count; %i ++ ) + { + %className = getField( %classes, %i ); + if( GuiEditor.isFilteredClass( %className ) + || !isMemberOfClass( %className, "GuiControl" ) ) + continue; + + %classDropdown.add( %className, 0 ); + } + + %classDropdown.sort(); + } + + %classDropdown.setText( "GuiControl" ); +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorNewGuiDialog::onWake( %this ) +{ + // Center the dialog. + + %root = %this.getRoot(); + %this.setPosition( %root.extent.x / 2 - %this.extent.x / 2, %root.extent.y / 2 - %this.extent.y / 2 ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorNewGuiDialog::onOK( %this ) +{ + %name = %this-->nameField.getValue(); + %class = %this-->classDropdown.getText(); + + // Make sure we don't clash with an existing object. + // If there's an existing GUIControl with the name, ask to replace. + // If there's an existing non-GUIControl with the name, or the name is invalid, refuse to create. + + if( isObject( %name ) && %name.isMemberOfClass( "GuiControl" ) ) + { + if( MessageBox( "Warning", "Replace the existing control '" @ %name @ "'?", "OkCancel", "Question" ) == $MROk ) + %name.delete(); + else + return; + } + + if( Editor::validateObjectName( %name, false ) ) + { + %this.getRoot().popDialog( %this ); + %obj = eval("return new " @ %class @ "(" @ %name @ ");"); + + // Make sure we have no association with a filename. + %obj.setFileName( "" ); + + GuiEditContent(%obj); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorNewGuiDialog::onCancel( %this ) +{ + %this.getRoot().popDialog( %this ); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorPrefsDlg.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorPrefsDlg.ed.cs new file mode 100644 index 000000000..c4713fc4e --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorPrefsDlg.ed.cs @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$GuiEditor::defaultGridSize = 8; +$GuiEditor::minGridSize = 3; + +//----------------------------------------------------------------------------------------- +// Buttons +//----------------------------------------------------------------------------------------- + +function GuiEditorPrefsDlgOkBtn::onAction(%this) +{ + GuiEditor.snap2gridsize = GuiEditorPrefsDlgGridEdit.getValue(); + if( GuiEditor.snap2grid ) + GuiEditor.setSnapToGrid( GuiEditor.snap2gridsize ); + + Canvas.popDialog( GuiEditorPrefsDlg ); +} + +function GuiEditorPrefsDlgCancelBtn::onAction(%this) +{ + Canvas.popDialog( GuiEditorPrefsDlg ); +} + +function GuiEditorPrefsDlgDefaultsBtn::onAction(%this) +{ + GuiEditorPrefsDlgGridSlider.setValue( $GuiEditor::defaultGridSize ); +} + +//----------------------------------------------------------------------------------------- +// Grid +//----------------------------------------------------------------------------------------- + +function GuiEditorPrefsDlgGridEdit::onWake(%this) +{ + %this.setValue( GuiEditor.snap2gridsize ); +} + +function GuiEditorPrefsDlgGridEdit::onAction( %this ) +{ + %value = %this.getValue(); + if( %value < $GuiEditor::minGridSize ) + { + %value = $GuiEditor::minGridSize; + %this.setValue( %value ); + } + + GuiEditorPrefsDlgGridSlider.setValue( %value ); +} + +function GuiEditorPrefsDlgGridSlider::onWake(%this) +{ + %this.setValue( GuiEditor.snap2gridsize ); +} + +function GuiEditorPrefsDlgGridSlider::onAction(%this) +{ + %value = %this.value; + if( %value < $GuiEditor::minGridSize ) + { + %value = $GuiEditor::minGridSize; + %this.setValue( %value ); + } + + GuiEditorPrefsDlgGridEdit.setvalue( mCeil( %value ) ); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorProfiles.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorProfiles.ed.cs new file mode 100644 index 000000000..997512b37 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorProfiles.ed.cs @@ -0,0 +1,623 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +$GUI_EDITOR_DEFAULT_PROFILE_FILENAME = "art/gui/customProfiles.cs"; +$GUI_EDITOR_DEFAULT_PROFILE_CATEGORY = "Other"; + + + +//============================================================================================= +// GuiEditor. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::createNewProfile( %this, %name, %copySource ) +{ + if( %name $= "" ) + return; + + // Make sure the object name is unique. + + if( isObject( %name ) ) + %name = getUniqueName( %name ); + + // Create the profile. + + if( %copySource !$= "" ) + eval( "new GuiControlProfile( " @ %name @ " : " @ %copySource.getName() @ " );" ); + else + eval( "new GuiControlProfile( " @ %name @ " );" ); + + // Add the item and select it. + + %category = %this.getProfileCategory( %name ); + %group = GuiEditorProfilesTree.findChildItemByName( 0, %category ); + + %id = GuiEditorProfilesTree.insertItem( %group, %name @ " (" @ %name.getId() @ ")", %name.getId(), "" ); + + GuiEditorProfilesTree.sort( 0, true, true, false ); + GuiEditorProfilesTree.clearSelection(); + GuiEditorProfilesTree.selectItem( %id ); + + // Mark it as needing to be saved. + + %this.setProfileDirty( %name, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::getProfileCategory( %this, %profile ) +{ + if( %this.isDefaultProfile( %name ) ) + return "Default"; + else if( %profile.category !$= "" ) + return %profile.category; + else + return $GUI_EDITOR_DEFAULT_PROFILE_CATEGORY; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::showDeleteProfileDialog( %this, %profile ) +{ + if( %profile $= "" ) + return; + + if( %profile.isInUse() ) + { + MessageBoxOk( "Error", + "The profile '" @ %profile.getName() @ "' is still used by Gui controls." + ); + return; + } + + MessageBoxYesNo( "Delete Profile?", + "Do you really want to delete '" @ %profile.getName() @ "'?", + "GuiEditor.deleteProfile( " @ %profile @ " );" + ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::deleteProfile( %this, %profile ) +{ + if( isObject( "GuiEditorProfilesPM" ) ) + new PersistenceManager( GuiEditorProfilesPM ); + + // Clear dirty state. + + %this.setProfileDirty( %profile, false ); + + // Remove from tree. + + %id = GuiEditorProfilesTree.findItemByValue( %profile.getId() ); + GuiEditorProfilesTree.removeItem( %id ); + + // Remove from file. + + GuiEditorProfilesPM.removeObjectFromFile( %profile ); + + // Delete profile object. + + %profile.delete(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::showSaveProfileDialog( %this, %currentFileName ) +{ + getSaveFileName( "TorqueScript Files|*.cs", %this @ ".doSaveProfile", %currentFileName ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::doSaveProfile( %this, %fileName ) +{ + %path = makeRelativePath( %fileName, getMainDotCsDir() ); + + GuiEditorProfileFileName.setText( %path ); + %this.saveProfile( GuiEditorProfilesTree.getSelectedProfile(), %path ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::saveProfile( %this, %profile, %fileName ) +{ + if( !isObject( "GuiEditorProfilesPM" ) ) + new PersistenceManager( GuiEditorProfilesPM ); + + if( !GuiEditorProfilesPM.isDirty( %profile ) + && ( %fileName $= "" || %fileName $= %profile.getFileName() ) ) + return; + + // Update the filename, if requested. + + if( %fileName !$= "" ) + { + %profile.setFileName( %fileName ); + GuiEditorProfilesPM.setDirty( %profile, %fileName ); + } + + // Save the object. + + GuiEditorProfilesPM.saveDirtyObject( %profile ); + + // Clear its dirty state. + + %this.setProfileDirty( %profile, false, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::revertProfile( %this, %profile ) +{ + // Revert changes. + + GuiEditorProfileChangeManager.revertEdits( %profile ); + + // Clear its dirty state. + + %this.setProfileDirty( %profile, false ); + + // Refresh inspector. + + if( GuiEditorProfileInspector.getInspectObject() == %profile ) + GuiEditorProfileInspector.refresh(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::isProfileDirty( %this, %profile ) +{ + if( !isObject( "GuiEditorProfilesPM" ) ) + return false; + + return GuiEditorProfilesPM.isDirty( %profile ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::setProfileDirty( %this, %profile, %value, %noCheck ) +{ + if( !isObject( "GuiEditorProfilesPM" ) ) + new PersistenceManager( GuiEditorProfilesPM ); + + if( %value ) + { + if( !GuiEditorProfilesPM.isDirty( %profile ) || %noCheck ) + { + // If the profile hasn't yet been associated with a file, + // put it in the default file. + + if( %profile.getFileName() $= "" ) + %profile.setFileName( $GUI_EDITOR_DEFAULT_PROFILE_FILENAME ); + + // Add the profile to the dirty set. + + GuiEditorProfilesPM.setDirty( %profile ); + + // Show the item as dirty in the tree. + + %id = GuiEditorProfilesTree.findItemByValue( %profile.getId() ); + GuiEditorProfilesTree.editItem( %id, GuiEditorProfilesTree.getItemText( %id ) SPC "*", %profile.getId() ); + + // Count the number of unsaved profiles. If this is + // the first one, indicate in the window title that + // we have unsaved profiles. + + %this.increaseNumDirtyProfiles(); + } + } + else + { + if( GuiEditorProfilesPM.isDirty( %profile ) || %noCheck ) + { + // Remove from dirty list. + + GuiEditorProfilesPM.removeDirty( %profile ); + + // Clear the dirty marker in the tree. + + %id = GuiEditorProfilesTree.findItemByValue( %profile.getId() ); + %text = GuiEditorProfilesTree.getItemText( %id ); + GuiEditorProfilesTree.editItem( %id, getSubStr( %text, 0, strlen( %text ) - 2 ), %profile.getId() ); + + // Count saved profiles. If this was the last unsaved profile, + // remove the unsaved changes indicator from the window title. + + %this.decreaseNumDirtyProfiles(); + + // Remove saved edits from the change manager. + + GuiEditorProfileChangeManager.clearEdits( %profile ); + } + } +} + +//--------------------------------------------------------------------------------------------- + +/// Return true if the given profile name is the default profile for a +/// GuiControl class or if it's the GuiDefaultProfile. +function GuiEditor::isDefaultProfile( %this, %name ) +{ + if( %name $= "GuiDefaultProfile" ) + return true; + + if( !endsWith( %name, "Profile" ) ) + return false; + + %className = getSubStr( %name, 0, strlen( %name ) - 7 ) @ "Ctrl"; + if( !isClass( %className ) ) + return false; + + return true; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::increaseNumDirtyProfiles( %this ) +{ + %this.numDirtyProfiles ++; + if( %this.numDirtyProfiles == 1 ) + { + %tab = GuiEditorTabBook-->profilesPage; + %tab.setText( %tab.text @ " *" ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditor::decreaseNumDirtyProfiles( %this ) +{ + %this.numDirtyProfiles --; + if( !%this.numDirtyProfiles ) + { + %tab = GuiEditorTabBook-->profilesPage; + %title = %tab.text; + %title = getSubstr( %title, 0, strlen( %title ) - 2 ); + + %tab.setText( %title ); + } +} + +//============================================================================================= +// GuiEditorProfilesTree. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::init( %this ) +{ + %this.clear(); + + %defaultGroup = %this.insertItem( 0, "Default", -1 ); + %otherGroup = %this.insertItem( 0, $GUI_EDITOR_DEFAULT_PROFILE_CATEGORY, -1 ); + + foreach( %obj in GuiDataGroup ) + { + if( !%obj.isMemberOfClass( "GuiControlProfile" ) ) + continue; + + // If it's an Editor profile, skip if showing them is not enabled. + + if( %obj.category $= "Editor" && !GuiEditor.showEditorProfiles ) + continue; + + // Create a visible name. + + %name = %obj.getName(); + if( %name $= "" ) + %name = "<Unnamed>"; + %text = %name @ " (" @ %obj.getId() @ ")"; + + // Find which group to put the control in. + + %isDefaultProfile = GuiEditor.isDefaultProfile( %name ); + if( %isDefaultProfile ) + %group = %defaultGroup; + else if( %obj.category !$= "" ) + { + %group = %this.findChildItemByName( 0, %obj.category ); + if( !%group ) + %group = %this.insertItem( 0, %obj.category ); + } + else + %group = %otherGroup; + + // Insert the item. + + %this.insertItem( %group, %text, %obj.getId(), "" ); + } + + %this.sort( 0, true, true, false ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::onSelect( %this, %id ) +{ + %obj = %this.getItemValue( %id ); + if( %obj == -1 ) + return; + + GuiEditorProfileInspector.inspect( %obj ); + + %fileName = %obj.getFileName(); + if( %fileName $= "" ) + %fileName = $GUI_EDITOR_DEFAULT_PROFILE_FILENAME; + + GuiEditorProfileFileName.setText( %fileName ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::onUnselect( %this, %id ) +{ + GuiEditorProfileInspector.inspect( 0 ); + GuiEditorProfileFileName.setText( "" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::onProfileRenamed( %this, %profile, %newName ) +{ + %item = %this.findItemByValue( %profile.getId() ); + if( %item == -1 ) + return; + + %newText = %newName @ " (" @ %profile.getId() @ ")"; + if( GuiEditor.isProfileDirty( %profile ) ) + %newText = %newText @ " *"; + + %this.editItem( %item, %newText, %profile.getId() ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::getSelectedProfile( %this ) +{ + return %this.getItemValue( %this.getSelectedItem() ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfilesTree::setSelectedProfile( %this, %profile ) +{ + %id = %this.findItemByValue( %profile.getId() ); + %this.selectItem( %id ); +} + +//============================================================================================= +// GuiEditorProfileInspector. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + GuiEditorProfileFieldInfo.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onFieldAdded( %this, %object, %fieldName ) +{ + GuiEditor.setProfileDirty( %object, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onFieldRemoved( %this, %object, %fieldName ) +{ + GuiEditor.setProfileDirty( %object, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onFieldRenamed( %this, %object, %oldFieldName, %newFieldName ) +{ + GuiEditor.setProfileDirty( %object, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + GuiEditor.setProfileDirty( %object, true ); + + // If it's the name field, make sure to sync up the treeview. + + if( %fieldName $= "name" ) + GuiEditorProfilesTree.onProfileRenamed( %object, %newValue ); + + // Add change record. + + GuiEditorProfileChangeManager.registerEdit( %object, %fieldName, %arrayIndex, %oldValue ); + + // Add undo. + + pushInstantGroup(); + + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %oldValue; + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + popInstantGroup(); + %action.addToManager( GuiEditor.getUndoManager() ); + GuiEditor.updateUndoMenu(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onInspectorPreFieldModification( %this, %fieldName, %arrayIndex ) +{ + pushInstantGroup(); + %undoManager = GuiEditor.getUndoManager(); + + %object = %this.getInspectObject(); + + %nameOrClass = %object.getName(); + if( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %object.getFieldValue( %fieldName, %arrayIndex ); + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + %this.currentFieldEditAction = %action; + popInstantGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onInspectorPostFieldModification( %this ) +{ + %action = %this.currentFieldEditAction; + %object = %action.objectId; + %fieldName = %action.fieldName; + %arrayIndex = %action.arrayIndex; + %oldValue = %action.fieldValue; + %newValue = %object.getFieldValue( %fieldName, %arrayIndex ); + + // If it's the name field, make sure to sync up the treeview. + + if( %action.fieldName $= "name" ) + GuiEditorProfilesTree.onProfileRenamed( %object, %newValue ); + + // Add change record. + + GuiEditorProfileChangeManager.registerEdit( %object, %fieldName, %arrayIndex, %oldValue ); + + %this.currentFieldEditAction.addToManager( GuiEditor.getUndoManager() ); + %this.currentFieldEditAction = ""; + + GuiEditor.updateUndoMenu(); + GuiEditor.setProfileDirty( %object, true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileInspector::onInspectorDiscardFieldModification( %this ) +{ + %this.currentFieldEditAction.undo(); + %this.currentFieldEditAction.delete(); + %this.currentFieldEditAction = ""; +} + +//============================================================================================= +// GuiEditorProfileChangeManager. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileChangeManager::registerEdit( %this, %profile, %fieldName, %arrayIndex, %oldValue ) +{ + // Early-out if we already have a registered edit on the same field. + + foreach( %obj in %this ) + { + if( %obj.profile != %profile ) + continue; + + if( %obj.fieldName $= %fieldName + && %obj.arrayIndex $= %arrayIndex ) + return; + } + + // Create a new change record. + + new ScriptObject() + { + parentGroup = %this; + profile = %profile; + fieldName = %fieldName; + arrayIndex = %arrayIndex; + oldValue = %oldValue; + }; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileChangeManager::clearEdits( %this, %profile ) +{ + for( %i = 0; %i < %this.getCount(); %i ++ ) + { + %obj = %this.getObject( %i ); + if( %obj.profile != %profile ) + continue; + + %obj.delete(); + %i --; + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileChangeManager::revertEdits( %this, %profile ) +{ + for( %i = 0; %i < %this.getCount(); %i ++ ) + { + %obj = %this.getObject( %i ); + if( %obj.profile != %profile ) + continue; + + %profile.setFieldValue( %obj.fieldName, %obj.oldValue, %obj.arrayIndex ); + + %obj.delete(); + %i --; + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorProfileChangeManager::getEdits( %this, %profile ) +{ + %set = new SimSet(); + + foreach( %obj in %this ) + if( %obj.profile == %profile ) + %set.add( %obj ); + + return %set; +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorSelectDlg.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorSelectDlg.ed.cs new file mode 100644 index 000000000..795cef4d3 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorSelectDlg.ed.cs @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::toggleVisibility( %this ) +{ + if( %this.isVisible() ) + %this.setVisible( false ); + else + %this.setVisible( true ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::getRootGroup( %this ) +{ + return GuiEditor.getContentControl(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::includeClass( %this, %className ) +{ + return ( isMemberOfClass( %className, "GuiControl" ) + && !GuiEditor.isFilteredClass( %className ) ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::selectObject( %this, %object, %val ) +{ + if( %val ) + GuiEditor.addSelection( %object ); + else + GuiEditor.removeSelection( %object ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::clearSelection( %this ) +{ + GuiEditor.clearSelection(); +} + +//============================================================================================= +// Events. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorSelectDlg::onVisible( %this, %visible ) +{ + if( !%visible ) + return; + + if( !%this.isInitialized ) + { + %this.init(); + %this.isInitialized = true; + } + + // Re-initialize the group list on each wake. + + %this.initGroupList(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorStatusBar.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorStatusBar.ed.cs new file mode 100644 index 000000000..5c8df8f5a --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorStatusBar.ed.cs @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Code for the status bar in the Gui Editor. + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorStatusBar::getMouseModeHelp( %this ) +{ + %isMac = ( $platform $= "macos" ); + if( %isMac ) + %cmdCtrl = "CMD"; + else + %cmdCtrl = "CTRL"; + + %mouseMode = GuiEditor.getMouseMode(); + switch$( %mouseMode ) + { + case "Selecting": + return ""; + + case "DragSelecting": + return %cmdCtrl @ " to add to selection; ALT to exclude parents; CTRL+ALT to exclude children"; + + case "MovingSelection": + return ""; + + case "SizingSelection": + return "CTRL to activate snapping; ALT to move instead of resize"; + + case "DragGuide": + return "Drag into ruler to delete; drop to place"; + } + + return ""; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorStatusBar::print( %this, %message ) +{ + %this.setText( %message ); + + %sequenceNum = %this.sequenceNum + 1; + %this.sequenceNum = %sequenceNum; + + %this.schedule( 4 * 1000, "clearMessage", %sequenceNum ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorStatusBar::clearMessage( %this, %sequenceNum ) +{ + // If we had no newer message in the meantime, clear + // out the current text. + + if( %this.sequenceNum == %sequenceNum ) + %this.setText( %this.getMouseModeHelp() ); +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorStatusBar::onWake( %this ) +{ + %this.setText( %this.getMouseModeHelp() ); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorToolbox.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorToolbox.ed.cs new file mode 100644 index 000000000..f8a67f2a9 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorToolbox.ed.cs @@ -0,0 +1,394 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Code for the toolbox tab of the Gui Editor sidebar. + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::initialize( %this ) +{ + // Set up contents. + + %viewType = %this.currentViewType; + if( %viewType $= "" ) + %viewType = "Categorized"; + + %this.currentViewType = ""; + %this.setViewType( %viewType ); + + %this.isInitialized = true; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::getViewType( %this ) +{ + return %this.currentViewType; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::setViewType( %this, %viewType ) +{ + if( %this.currentViewType $= %viewType + || !%this.isMethod( "setViewType" @ %viewType ) ) + return; + + %this.clear(); + eval( %this @ ".setViewType" @ %viewType @ "();" ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::setViewTypeAlphabetical( %this ) +{ + %controls = enumerateConsoleClassesByCategory( "Gui" ); + %classes = new ArrayObject(); + + // Collect relevant classes. + + foreach$( %className in %controls ) + { + if( GuiEditor.isFilteredClass( %className ) + || !isMemberOfClass( %className, "GuiControl" ) ) + continue; + + %classes.push_back( %className ); + } + + // Sort classes alphabetically. + + %classes.sortk( true ); + + // Add toolbox buttons. + + %numClasses = %classes.count(); + for( %i = 0; %i < %numClasses; %i ++ ) + { + %className = %classes.getKey( %i ); + %ctrl = new GuiIconButtonCtrl() + { + profile = "ToolsGuiIconButtonSmallProfile"; + extent = "128 18"; + text = %className; + iconBitmap = EditorIconRegistry::findIconByClassName( %className ); + buttonMargin = "2 2"; + iconLocation = "left"; + textLocation = "left"; + textMargin = "24"; + AutoSize = true; + + command = "GuiEditor.createControl( " @ %className @ " );"; + useMouseEvents = true; + className = "GuiEditorToolboxButton"; + tooltip = %className NL "\n" @ getDescriptionOfClass( %className ); + tooltipProfile = "ToolsGuiToolTipProfile"; + }; + + %this.add( %ctrl ); + } + + %classes.delete(); + %this.currentViewType = "Alphabetical"; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::setViewTypeCategorized( %this ) +{ + // Create rollouts for each class category we have and + // record the classes in each category in a temporary array + // on the rollout so we can later sort the class names before + // creating the actual controls in the toolbox. + + %controls = enumerateConsoleClassesByCategory( "Gui" ); + foreach$( %className in %controls ) + { + if( GuiEditor.isFilteredClass( %className ) + || !isMemberOfClass( %className, "GuiControl" ) ) + continue; + + // Get the class's next category under Gui. + + %category = getWord( getCategoryOfClass( %className ), 1 ); + if( %category $= "" ) + continue; + + // Find or create the rollout for the category. + + %rollout = %this.getOrCreateRolloutForCategory( %category ); + + // Insert the item. + + if( !%rollout.classes ) + %rollout.classes = new ArrayObject(); + + %rollout.classes.push_back( %className ); + } + + // Go through the rollouts, sort the class names, and + // create the toolbox controls. + + foreach( %rollout in %this ) + { + if( !%rollout.isMemberOfClass( "GuiRolloutCtrl" ) ) + continue; + + // Get the array with the class names and sort it. + // Sort in descending order to counter reversal of order + // when we later add the controls to the stack. + + %classes = %rollout.classes; + %classes.sortk( true ); + + // Add a control for each of the classes to the + // rollout's stack control. + + %stack = %rollout-->array; + %numClasses = %classes.count(); + for( %n = 0; %n < %numClasses; %n ++ ) + { + %className = %classes.getKey( %n ); + %ctrl = new GuiIconButtonCtrl() + { + profile = "ToolsGuiIconButtonSmallProfile"; + extent = "128 18"; + text = %className; + iconBitmap = EditorIconRegistry::findIconByClassName( %className ); + buttonMargin = "2 2"; + iconLocation = "left"; + textLocation = "left"; + textMargin = "24"; + AutoSize = true; + + command = "GuiEditor.createControl( " @ %className @ " );"; + useMouseEvents = true; + className = "GuiEditorToolboxButton"; + tooltip = %className NL "\n" @ getDescriptionOfClass( %className ); + tooltipProfile = "ToolsGuiToolTipProfile"; + }; + + %stack.add( %ctrl ); + } + + // Delete the temporary array. + + %rollout.classes = ""; + %classes.delete(); + } + + %this.currentViewType = "Categorized"; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::getOrCreateRolloutForCategory( %this, %category ) +{ + // Try to find an existing rollout. + + %ctrl = %this.getRolloutForCategory( %category ); + if( %ctrl != 0 ) + return %ctrl; + + // None there. Create a new one. + + %ctrl = new GuiRolloutCtrl() { + Margin = "0 0 0 0"; + DefaultHeight = "40"; + Expanded = "1"; + ClickCollapse = "1"; + HideHeader = "0"; + isContainer = "1"; + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "421 114"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + autoCollapseSiblings = true; + caption = %category; + class = "GuiEditorToolboxRolloutCtrl"; + + new GuiDynamicCtrlArrayControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "421 64"; + MinExtent = "64 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + padding = "6 2 4 0"; + colSpacing = "1"; + rowSpacing = "9"; + dynamicSize = true; + autoCellSize = true; + internalName = "array"; + }; + }; + + %this.add( %ctrl ); + %ctrl.collapse(); + + // Sort the rollouts by their caption. + + %this.sort( "_GuiEditorToolboxSortRollouts" ); + + return %ctrl; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::getRolloutForCategory( %this, %category ) +{ + foreach( %obj in %this ) + { + if( !%obj.isMemberOfClass( "GuiRolloutCtrl" ) ) + continue; + + if( stricmp( %obj.caption, %category ) == 0 ) + return %obj; + } + + return 0; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolbox::startGuiControlDrag( %this, %class ) +{ + // Create a new control of the given class. + + %payload = eval( "return new " @ %class @ "();" ); + if( !isObject( %payload ) ) + return; + + // this offset puts the cursor in the middle of the dragged object. + %xOffset = getWord( %payload.extent, 0 ) / 2; + %yOffset = getWord( %payload.extent, 1 ) / 2; + + // position where the drag will start, to prevent visible jumping. + %cursorpos = Canvas.getCursorPos(); + %xPos = getWord( %cursorpos, 0 ) - %xOffset; + %yPos = getWord( %cursorpos, 1 ) - %yOffset; + + // Create drag&drop control. + + %dragCtrl = new GuiDragAndDropControl() + { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = %xPos SPC %yPos; + extent = %payload.extent; + MinExtent = "32 32"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + deleteOnMouseUp = true; + class = "GuiDragAndDropControlType_GuiControl"; + }; + + %dragCtrl.add( %payload ); + Canvas.getContent().add( %dragCtrl ); + + // Start drag. + + %dragCtrl.startDragging( %xOffset, %yOffset ); +} + +//============================================================================================= +// GuiEditorToolboxRolloutCtrl. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolboxRolloutCtrl::onHeaderRightClick( %this ) +{ + if( !isObject( GuiEditorToolboxRolloutCtrlMenu ) ) + new PopupMenu( GuiEditorToolboxRolloutCtrlMenu ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Expand All" TAB "" TAB %this @ ".expandAll();"; + item[ 1 ] = "Collapse All" TAB "" TAB %this @ ".collapseAll();"; + }; + + GuiEditorToolboxRolloutCtrlMenu.showPopup( %this.getRoot() ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolboxRolloutCtrl::expandAll( %this ) +{ + foreach( %ctrl in %this.parentGroup ) + { + if( %ctrl.isMemberOfClass( "GuiRolloutCtrl" ) ) + %ctrl.instantExpand(); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolboxRolloutCtrl::collapseAll( %this ) +{ + foreach( %ctrl in %this.parentGroup ) + { + if( %ctrl.isMemberOfClass( "GuiRolloutCtrl" ) ) + %ctrl.instantCollapse(); + } +} + +//============================================================================================= +// GuiEditorToolboxButton. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function GuiEditorToolboxButton::onMouseDragged( %this ) +{ + GuiEditorToolbox.startGuiControlDrag( %this.text ); +} + +//============================================================================================= +// Misc. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +/// Utility function to sort rollouts by their caption. +function _GuiEditorToolboxSortRollouts( %a, %b ) +{ + return strinatcmp( %a.caption, %b.caption ); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorTreeView.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorTreeView.ed.cs new file mode 100644 index 000000000..82a14bea0 --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorTreeView.ed.cs @@ -0,0 +1,220 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Code for the main Gui Editor tree view that shows the hierarchy of the +// current GUI being edited. + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::init(%this) +{ + if( !isObject( %this.contextMenu ) ) + %this.contextMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Rename" TAB "" TAB "GuiEditorTreeView.showItemRenameCtrl( GuiEditorTreeView.findItemByObjectId( %this.object ) );"; + item[ 1 ] = "Delete" TAB "" TAB "GuiEditor.deleteControl( %this.object );"; + item[ 2 ] = "-"; + item[ 3 ] = "Locked" TAB "" TAB "%this.object.setLocked( !%this.object.locked ); GuiEditorTreeView.update();"; + item[ 4 ] = "Hidden" TAB "" TAB "%this.object.setVisible( !%this.object.isVisible() ); GuiEditorTreeView.update();"; + item[ 5 ] = "-"; + item[ 6 ] = "Add New Controls Here" TAB "" TAB "GuiEditor.setCurrentAddSet( %this.object );"; + item[ 7 ] = "Add Child Controls to Selection" TAB "" TAB "GuiEditor.selectAllControlsInSet( %this.object, false );"; + item[ 8 ] = "Remove Child Controls from Selection" TAB "" TAB "GuiEditor.selectAllControlsInSet( %this.object, true );"; + + object = -1; + }; + + if( !isObject( %this.contextMenuMultiSel ) ) + %this.contextMenuMultiSel = new PopupMenu() + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Delete" TAB "" TAB "GuiEditor.deleteSelection();"; + }; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::update( %this ) +{ + %obj = GuiEditorContent.getObject( 0 ); + + if( !isObject( %obj ) ) + GuiEditorTreeView.clear(); + else + { + // Open inspector tree. + + GuiEditorTreeView.open( %obj ); + + // Sync selection with GuiEditor. + + GuiEditorTreeView.clearSelection(); + + %selection = GuiEditor.getSelection(); + %count = %selection.getCount(); + + for( %i = 0; %i < %count; %i ++ ) + GuiEditorTreeView.addSelection( %selection.getObject( %i ) ); + } +} + +//============================================================================================= +// Event Handlers. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +/// Defines the icons to be used in the tree view control. +/// Provide the paths to each icon minus the file extension. +/// Seperate them with ':'. +/// The order of the icons must correspond to the bit array defined +/// in the GuiTreeViewCtrl.h. +function GuiEditorTreeView::onDefineIcons(%this) +{ + %icons = ":" @ // Default1 + ":" @ // SimGroup1 + ":" @ // SimGroup2 + ":" @ // SimGroup3 + ":" @ // SimGroup4 + "tools/gui/images/treeview/hidden:" @ + "tools/worldEditor/images/lockedHandle"; + + GuiEditorTreeView.buildIconTable( %icons ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onRightMouseDown( %this, %item, %pts, %obj ) +{ + if( %this.getSelectedItemsCount() > 1 ) + { + %popup = %this.contextMenuMultiSel; + %popup.showPopup( Canvas ); + } + else if( %obj ) + { + %popup = %this.contextMenu; + + %popup.checkItem( 3, %obj.locked ); + %popup.checkItem( 4, !%obj.isVisible() ); + + %popup.enableItem( 6, %obj.isContainer ); + %popup.enableItem( 7, %obj.getCount() > 0 ); + %popup.enableItem( 8, %obj.getCount() > 0 ); + + %popup.object = %obj; + %popup.showPopup( Canvas ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onAddSelection(%this,%ctrl) +{ + GuiEditor.dontSyncTreeViewSelection = true; + GuiEditor.addSelection( %ctrl ); + GuiEditor.dontSyncTreeViewSelection = false; + GuiEditor.setFirstResponder(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onRemoveSelection( %this, %ctrl ) +{ + GuiEditor.dontSyncTreeViewSelection = true; + GuiEditor.removeSelection( %ctrl ); + GuiEditor.dontSyncTreeViewSelection = false; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onDeleteSelection(%this) +{ + GuiEditor.clearSelection(); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onSelect( %this, %obj ) +{ + if( isObject( %obj ) ) + { + GuiEditor.dontSyncTreeViewSelection = true; + GuiEditor.select( %obj ); + GuiEditor.dontSyncTreeViewSelection = false; + GuiEditorInspectFields.update( %obj ); + } +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::isValidDragTarget( %this, %id, %obj ) +{ + return ( %obj.isContainer || %obj.getCount() > 0 ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onBeginReparenting( %this ) +{ + if( isObject( %this.reparentUndoAction ) ) + %this.reparentUndoAction.delete(); + + %action = UndoActionReparentObjects::create( %this ); + + %this.reparentUndoAction = %action; +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onReparent( %this, %obj, %oldParent, %newParent ) +{ + %this.reparentUndoAction.add( %obj, %oldParent, %newParent ); +} + +//--------------------------------------------------------------------------------------------- + +function GuiEditorTreeView::onEndReparenting( %this ) +{ + %action = %this.reparentUndoAction; + %this.reparentUndoAction = ""; + + if( %action.numObjects > 0 ) + { + if( %action.numObjects == 1 ) + %action.actionName = "Reparent Control"; + else + %action.actionName = "Reparent Controls"; + + %action.addToManager( GuiEditor.getUndoManager() ); + + GuiEditor.updateUndoMenu(); + } + else + %action.delete(); +} diff --git a/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorUndo.ed.cs b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorUndo.ed.cs new file mode 100644 index 000000000..fad91557e --- /dev/null +++ b/Templates/BaseGame/game/tools/guiEditor/scripts/guiEditorUndo.ed.cs @@ -0,0 +1,598 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------------------------- + +function GuiEditorUndoManager::onAddUndo( %this ) +{ + GuiEditor.updateUndoMenu(); +} + +//----------------------------------------------------------------------------------------- +// Undo adding an object +function UndoActionAddObject::create( %set, %trash, %treeView ) +{ + %act = UndoActionAddDelete::create( UndoActionAddObject, %set, %trash, %treeView ); + %act.actionName = "Add Objects"; + return %act; +} + +function UndoActionAddObject::undo(%this) +{ + %this.trashObjects(); +} + +function UndoActionAddObject::redo(%this) +{ + %this.restoreObjects(); +} + +//----------------------------------------------------------------------------------------- +// Undo Deleting an object +function UndoActionDeleteObject::create( %set, %trash, %treeView ) +{ + %act = UndoActionAddDelete::create( UndoActionDeleteObject, %set, %trash, %treeView, true ); + %act.designatedDeleter = true; + %act.actionName = "Delete Objects"; + return %act; +} + +function UndoActionDeleteObject::undo( %this ) +{ + %this.restoreObjects(); +} + +function UndoActionDeleteObject::redo( %this ) +{ + %this.trashObjects(); +} + +//----------------------------------------------------------------------------------------- +// Behavior common to Add and Delete UndoActions +function UndoActionAddDelete::create( %class, %set, %trash, %treeView, %clearNames ) +{ + // record objects + // record parents + // record trash + // return new subclass %class of UndoActionAddDelete + + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + %act = new UndoScriptAction() { class = %class; superclass = UndoActionAddDelete; }; + + // Restore the instant group. + popInstantGroup(); + + for(%i = 0; %i < %set.getCount(); %i++) + { + %obj = %set.getObject(%i); + + %act.object[ %i ] = %obj.getId(); + %act.parent[ %i ] = %obj.getParent(); + %act.objectName[ %i ] = %obj.name; + + // Clear object name so we don't get name clashes with the trash. + + if( %clearNames ) + %obj.name = ""; + } + + %act.objCount = %set.getCount(); + %act.trash = %trash; + %act.tree = %treeView; + + return %act; +} + +function UndoActionAddDelete::trashObjects(%this) +{ + // Move objects to trash. + + for( %i = 0; %i < %this.objCount; %i ++ ) + { + %object = %this.object[ %i ]; + + %this.trash.add( %object ); + %object.name = ""; + } + + // Note that we're responsible for deleting those objects we've moved to the trash. + + %this.designatedDeleter = true; + + // Update the tree view. + + if( isObject( %this.tree ) ) + %this.tree.update(); +} + +function UndoActionAddDelete::restoreObjects(%this) +{ + // Move objects to saved parent and restore names. + + for( %i = 0; %i < %this.objCount; %i ++ ) + { + %object = %this.object[ %i ]; + %object.name = %this.objectName[ %i ]; + %this.parent[ %i ].add( %object ); + } + + // Note that we no longer own the objects, and should not delete them when we're deleted. + + %this.designatedDeleter = false; + + // Update the tree view. + + if( isObject( %this.tree ) ) + %this.tree.update(); +} + +function UndoActionAddObject::onRemove(%this) +{ + // if this undoAction owns objects in the trash, delete them. + if( !%this.designatedDeleter) + return; + + for( %i = 0; %i < %this.objCount; %i ++) + %this.object[ %i ].delete(); +} + +//----------------------------------------------------------------------------------------- +// Undo grouping/ungrouping of controls. + +function GuiEditorGroupUngroupAction::groupControls( %this ) +{ + for( %i = 0; %i < %this.count; %i ++ ) + %this.group[ %i ].group(); + + GuiEditorTreeView.update(); +} + +function GuiEditorGroupUngroupAction::ungroupControls( %this ) +{ + for( %i = 0; %i < %this.count; %i ++ ) + %this.group[ %i ].ungroup(); + + GuiEditorTreeView.update(); +} + +function GuiEditorGroupUngroupAction::onRemove( %this ) +{ + for( %i = 0; %i < %this.count; %i ++ ) + if( isObject( %this.group[ %i ] ) ) + %this.group[ %i ].delete(); +} + +function GuiEditorGroupAction::create( %set, %root ) +{ + // Create action object. + + pushInstantGroup(); + %action = new UndoScriptAction() + { + actionName = "Group"; + className = GuiEditorGroupAction; + superClass = GuiEditorGroupUngroupAction; + count = 1; + group[ 0 ] = new ScriptObject() + { + className = GuiEditorGroup; + count = %set.getCount(); + groupParent = GuiEditor.getCurrentAddSet(); + }; + }; + popInstantGroup(); + + // Add objects from set to group. + + %group = %action.group[ 0 ]; + %num = %set.getCount(); + for( %i = 0; %i < %num; %i ++ ) + { + %ctrl = %set.getObject( %i ); + if( %ctrl != %root ) + %group.ctrl[ %i ] = %ctrl; + } + + return %action; +} + +function GuiEditorGroupAction::undo( %this ) +{ + %this.ungroupControls(); +} + +function GuiEditorGroupAction::redo( %this ) +{ + %this.groupControls(); +} + +function GuiEditorUngroupAction::create( %set, %root ) +{ + // Create action object. + + pushInstantGroup(); + %action = new UndoScriptAction() + { + actionName = "Ungroup"; + className = GuiEditorUngroupAction; + superClass = GuiEditorGroupUngroupAction; + }; + + // Add groups from set to action. + + %groupCount = 0; + %numInSet = %set.getCount(); + for( %i = 0; %i < %numInSet; %i ++ ) + { + %obj = %set.getObject( %i ); + if( %obj.getClassName() $= "GuiControl" && %obj != %root ) + { + // Create group object. + + %group = new ScriptObject() + { + className = GuiEditorGroup; + count = %obj.getCount(); + groupParent = %obj.parentGroup; + groupObject = %obj; + }; + %action.group[ %groupCount ] = %group; + %groupCount ++; + + // Add controls. + + %numControls = %obj.getCount(); + for( %j = 0; %j < %numControls; %j ++ ) + %group.ctrl[ %j ] = %obj.getObject( %j ); + } + } + + popInstantGroup(); + + %action.count = %groupCount; + return %action; +} + +function GuiEditorUngroupAction::undo( %this ) +{ + %this.groupControls(); +} + +function GuiEditorUngroupAction::redo( %this ) +{ + %this.ungroupControls(); +} + +//------------------------------------------------------------------------------ +// Undo Any State Change. +function GenericUndoAction::create() +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + %act = new UndoScriptAction() { class = GenericUndoAction; }; + %act.actionName = "Edit Objects"; + + // Restore the instant group. + popInstantGroup(); + + return %act; +} + +function GenericUndoAction::watch(%this, %object) +{ + // make sure we're working with the object id, because it cannot change. + %object = %object.getId(); + + %fieldCount = %object.getFieldCount(); + %dynFieldCount = %object.getDynamicFieldCount(); + + // inspect all the fields on the object, including dyanamic ones. + // record field names and values. + for(%i = 0; %i < %fieldCount; %i++) + { + %field = %object.getField(%i); + %this.fieldNames[%object] = %this.fieldNames[%object] SPC %field; + %this.fieldValues[%object, %field] = %object.getFieldValue(%field); + } + for(%i = 0; %i < %dynFieldCount; %i++) + { + %field = %object.getDynamicField(%i); + %this.fieldNames[%object] = %this.fieldNames[%object] SPC %field; + %this.fieldValues[%object, %field] = %object.getFieldValue(%field); + } + // clean spurious spaces from the field name list + %this.fieldNames[%object] = trim(%this.fieldNames[%object]); + // record that we know this object + %this.objectIds[%object] = 1; + %this.objectIdList = %this.objectIdList SPC %object; +} + +function GenericUndoAction::learn(%this, %object) +{ + // make sure we're working with the object id, because it cannot change. + %object = %object.getId(); + + %fieldCount = %object.getFieldCount(); + %dynFieldCount = %object.getDynamicFieldCount(); + + // inspect all the fields on the object, including dyanamic ones. + // record field names and values. + for(%i = 0; %i < %fieldCount; %i++) + { + %field = %object.getField(%i); + %this.newFieldNames[%object] = %this.newFieldNames[%object] SPC %field; + %this.newFieldValues[%object, %field] = %object.getFieldValue(%field); + } + for(%i = 0; %i < %dynFieldCount; %i++) + { + %field = %object.getDynamicField(%i); + %this.newFieldNames[%object] = %this.newFieldNames[%object] SPC %field; + %this.newFieldValues[%object, %field] = %object.getFieldValue(%field); + } + // trim + %this.newFieldNames[%object] = trim(%this.newFieldNames[%object]); + + // look for differences + //---------------------------------------------------------------------- + %diffs = false; + %newFieldNames = %this.newFieldNames[%object]; + %oldFieldNames = %this.fieldNames[%object]; + %numNewFields = getWordCount(%newFieldNames); + %numOldFields = getWordCount(%oldFieldNames); + // compare the old field list to the new field list. + // if a field is on the old list that isn't on the new list, + // add it to the newNullFields list. + for(%i = 0; %i < %numOldFields; %i++) + { + %field = getWord(%oldFieldNames, %i); + %newVal = %this.newFieldValues[%object, %field]; + %oldVal = %this.fieldValues[%object, %field]; + if(%newVal !$= %oldVal) + { + %diffs = true; + if(%newVal $= "") + { + %newNullFields = %newNullFields SPC %field; + } + } + } + // scan the new field list + // add missing fields to the oldNullFields list + for(%i = 0; %i < %numNewFields; %i++) + { + %field = getWord(%newFieldNames, %i); + %newVal = %this.newFieldValues[%object, %field]; + %oldVal = %this.fieldValues[%object, %field]; + if(%newVal !$= %oldVal) + { + %diffs = true; + if(%oldVal $= "") + { + %oldNullFields = %oldNullFields SPC %field; + } + } + } + %this.newNullFields[%object] = trim(%newNullFields); + %this.oldNullFields[%object] = trim(%oldNullFields); + + return %diffs; +} + +function GenericUndoAction::watchSet(%this, %set) +{ + // scan the set + // this.watch each object. + %setcount = %set.getCount(); + %i = 0; + for(; %i < %setcount; %i++) + { + %object = %set.getObject(%i); + %this.watch(%object); + } +} + +function GenericUndoAction::learnSet(%this, %set) +{ + // scan the set + // this.learn any objects that we have a this.objectIds[] entry for. + %diffs = false; + for(%i = 0; %i < %set.getCount(); %i++) + { + %object = %set.getObject(%i).getId(); + if(%this.objectIds[%object] != 1) + continue; + + if(%this.learn(%object)) + %diffs = true; + } + + return %diffs; +} + +function GenericUndoAction::undo(%this) +{ + // set the objects to the old values + // scan through our objects + %objectList = %this.objectIdList; + for(%i = 0; %i < getWordCount(%objectList); %i++) + { + %object = getWord(%objectList, %i); + // scan through the old extant fields + %fieldNames = %this.fieldNames[%object]; + for(%j = 0; %j < getWordCount(%fieldNames); %j++) + { + %field = getWord(%fieldNames, %j); + %object.setFieldValue(%field, %this.fieldValues[%object, %field]); + } + // null out the fields in the null list + %fieldNames = %this.oldNullFields[%object]; + for(%j = 0; %j < getWordCount(%fieldNames); %j++) + { + %field = getWord(%fieldNames, %j); + %object.setFieldValue(%field, ""); + } + } + + // update the tree view + if(isObject(%this.tree)) + %this.tree.update(); +} + +function GenericUndoAction::redo(%this) +{ + // set the objects to the new values + // set the objects to the new values + // scan through our objects + %objectList = %this.objectIdList; + for(%i = 0; %i < getWordCount(%objectList); %i++) + { + %object = getWord(%objectList, %i); + // scan through the new extant fields + %fieldNames = %this.newFieldNames[%object]; + for(%j = 0; %j < getWordCount(%fieldNames); %j++) + { + %field = getWord(%fieldNames, %j); + %object.setFieldValue(%field, %this.newFieldValues[%object, %field]); + } + // null out the fields in the null list + %fieldNames = %this.newNullFields[%object]; + for(%j = 0; %j < getWordCount(%fieldNames); %j++) + { + %field = getWord(%fieldNames, %j); + %object.setFieldValue(%field, ""); + } + } + + // update the tree view + if(isObject(%this.tree)) + %this.tree.update(); +} + +//----------------------------------------------------------------------------------------- +// Gui Editor Undo hooks from code +function GuiEditor::onPreEdit(%this, %selection) +{ + if ( isObject(%this.pendingGenericUndoAction) ) + { + error("Error: attempting to create two generic undo actions at once in the same editor!"); + return; + } + + //echo("pre edit"); + %act = GenericUndoAction::create(); + %act.watchSet(%selection); + %act.tree = GuiEditorTreeView; + + %this.pendingGenericUndoAction = %act; + + %this.updateUndoMenu(); +} + +function GuiEditor::onPostEdit(%this, %selection) +{ + if(!isObject(%this.pendingGenericUndoAction)) + error("Error: attempting to complete a GenericUndoAction that hasn't been started!"); + + %act = %this.pendingGenericUndoAction; + %this.pendingGenericUndoAction = ""; + + %diffs = %act.learnSet(%selection); + if(%diffs) + { + //echo("adding generic undoaction to undo manager"); + //%act.dump(); + %act.addToManager(%this.getUndoManager()); + } + else + { + //echo("deleting empty generic undoaction"); + %act.delete(); + } + + %this.updateUndoMenu(); +} + +function GuiEditor::onPreSelectionNudged(%this, %selection) +{ + %this.onPreEdit(%selection); + %this.pendingGenericUndoAction.actionName = "Nudge"; +} + +function GuiEditor::onPostSelectionNudged(%this, %selection) +{ + %this.onPostEdit(%selection); +} + +function GuiEditor::onAddNewCtrl(%this, %ctrl) +{ + %set = new SimSet(); + %set.add(%ctrl); + %act = UndoActionAddObject::create(%set, %this.getTrash(), GuiEditorTreeView); + %set.delete(); + %act.addToManager(%this.getUndoManager()); + %this.updateUndoMenu(); + //GuiEditorInspectFields.update(0); +} + +function GuiEditor::onAddNewCtrlSet(%this, %selection) +{ + %act = UndoActionAddObject::create(%selection, %this.getTrash(), GuiEditorTreeView); + %act.addToManager(%this.getUndoManager()); + %this.updateUndoMenu(); +} + +function GuiEditor::onTrashSelection(%this, %selection) +{ + %act = UndoActionDeleteObject::create(%selection, %this.getTrash(), GuiEditorTreeView); + %act.addToManager(%this.getUndoManager()); + %this.updateUndoMenu(); +} + +function GuiEditor::onControlInspectPreApply(%this, %object) +{ + %set = new SimSet(); + %set.add(%object); + %this.onPreEdit(%set); + %this.pendingGenericUndoAction.actionName = "Change Properties"; + %set.delete(); +} + +function GuiEditor::onControlInspectPostApply(%this, %object) +{ + %set = new SimSet(); + %set.add(%object); + %this.onPostEdit(%set); + %set.delete(); + GuiEditorTreeView.update(); +} + +function GuiEditor::onFitIntoParents( %this ) +{ + %selected = %this.getSelection(); + //TODO +} diff --git a/Templates/BaseGame/game/tools/levels/BlankRoom.mis b/Templates/BaseGame/game/tools/levels/BlankRoom.mis new file mode 100644 index 000000000..5ca65ed5a --- /dev/null +++ b/Templates/BaseGame/game/tools/levels/BlankRoom.mis @@ -0,0 +1,94 @@ +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + canSaveDynamicFields = "1"; + cdTrack = "2"; + CTF_scoreLimit = "5"; + Enabled = "1"; + musicTrack = "lush"; + + new LevelInfo(theLevelInfo) { + visibleDistance = "1000"; + fogColor = "0.6 0.6 0.7 1"; + fogDensity = "0"; + fogDensityOffset = "700"; + fogAtmosphereHeight = "0"; + canvasClearColor = "0 0 0 255"; + canSaveDynamicFields = "1"; + levelName = "Blank Room"; + desc0 = "A blank room ready to be populated with Torque objects.\n\nGuns, anyone?"; + Enabled = "1"; + }; + new SkyBox(theSky) { + canSaveDynamicFields = "1"; + Position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + Material = "BlackSkyMat"; + drawBottom = "0"; + fogBandHeight = "0"; + }; + new Sun(theSun) { + canSaveDynamicFields = "1"; + Position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + azimuth = "230.396"; + elevation = "45"; + color = "0.968628 0.901961 0.901961 1"; + ambient = "0.078431 0.113725 0.156863 1"; + castShadows = "1"; + attenuationRatio = "0 1 1"; + shadowType = "PSSM"; + texSize = "1024"; + overDarkFactor = "3000 1500 750 250"; + shadowDistance = "200"; + shadowSoftness = "0.25"; + numSplits = "4"; + logWeight = "0.9"; + fadeStartDistance = "0"; + lastSplitTerrainOnly = "0"; + splitFadeDistances = "1 1 1 1"; + bias = "0.1"; + Blur = "1"; + Enabled = "1"; + height = "1024"; + lightBleedFactor = "0.8"; + minVariance = "0"; + pointShadowType = "PointShadowType_Paraboloid"; + shadowBox = "-100 -100 -100 100 100 100"; + width = "3072"; + }; + new SimGroup(PlayerDropPoints) { + canSaveDynamicFields = "1"; + Enabled = "1"; + + new SpawnSphere() { + canSaveDynamicFields = "1"; + Position = "0 0 2"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + radius = "5"; + autoSpawn = "false"; + sphereWeight = "1"; + indoorWeight = "1"; + outdoorWeight = "1"; + Enabled = "1"; + homingCount = "0"; + lockCount = "0"; + }; + }; + new GroundPlane() { + canSaveDynamicFields = "1"; + Position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + squareSize = "128"; + scaleU = "12"; + scaleV = "12"; + Material = "BlankWhite"; + Enabled = "1"; + }; +}; +//--- OBJECT WRITE END --- + diff --git a/Templates/BaseGame/game/tools/levels/BlankRoom_preview.png b/Templates/BaseGame/game/tools/levels/BlankRoom_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..1d91f60f792d85b7579af6e033648fa3f5613cf6 GIT binary patch literal 12867 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/main.cs b/Templates/BaseGame/game/tools/main.cs new file mode 100644 index 000000000..d1a0df811 --- /dev/null +++ b/Templates/BaseGame/game/tools/main.cs @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------------------- +// Path to the folder that contains the editors we will load. +//--------------------------------------------------------------------------------------------- +$Tools::resourcePath = "tools/"; + +// These must be loaded first, in this order, before anything else is loaded +$Tools::loadFirst = "editorClasses base worldEditor"; + +//--------------------------------------------------------------------------------------------- +// Object that holds the simObject id that the materialEditor uses to interpret its material list +//--------------------------------------------------------------------------------------------- +$Tools::materialEditorList = ""; + +//--------------------------------------------------------------------------------------------- +// Tools Package. +//--------------------------------------------------------------------------------------------- +package Tools +{ + function loadKeybindings() + { + Parent::loadKeybindings(); + } + + // Start-up. + function onStart() + { + //First, we want to ensure we don't inadvertently clean up our editor objects by leaving them in the MissionCleanup group, so lets change that real fast + $instantGroup = ""; + pushInstantGroup(); + + new Settings(EditorSettings) { file = "tools/settings.xml"; }; + EditorSettings.read(); + + echo( " % - Initializing Tools" ); + + // Default file path when saving from the editor (such as prefabs) + if ($Pref::WorldEditor::LastPath $= "") + { + $Pref::WorldEditor::LastPath = getMainDotCsDir(); + } + + // Common GUI stuff. + exec( "./gui/cursors.ed.cs" ); + exec( "./gui/profiles.ed.cs" ); + exec( "./gui/messageBoxes/messageBox.ed.cs" ); + exec( "./editorClasses/gui/panels/navPanelProfiles.ed.cs" ); + + // Make sure we get editor profiles before any GUI's + // BUG: these dialogs are needed earlier in the init sequence, and should be moved to + // common, along with the guiProfiles they depend on. + exec( "./gui/guiDialogs.ed.cs" ); + + //%toggle = $Scripts::ignoreDSOs; + //$Scripts::ignoreDSOs = true; + + $ignoredDatablockSet = new SimSet(); + + // fill the list of editors + $editors[count] = getWordCount( $Tools::loadFirst ); + for ( %i = 0; %i < $editors[count]; %i++ ) + { + $editors[%i] = getWord( $Tools::loadFirst, %i ); + } + + %pattern = $Tools::resourcePath @ "/*/main.cs"; + %folder = findFirstFile( %pattern ); + if ( %folder $= "") + { + // if we have absolutely no matches for main.cs, we look for main.cs.dso + %pattern = $Tools::resourcePath @ "/*/main.cs.dso"; + %folder = findFirstFile( %pattern ); + } + while ( %folder !$= "" ) + { + if( filePath( %folder ) !$= "tools" ) // Skip the actual 'tools' folder...we want the children + { + %folder = filePath( %folder ); + %editor = fileName( %folder ); + if ( IsDirectory( %folder ) ) + { + // Yes, this sucks and should be done better + if ( strstr( $Tools::loadFirst, %editor ) == -1 ) + { + $editors[$editors[count]] = %editor; + $editors[count]++; + } + } + } + %folder = findNextFile( %pattern ); + } + + // initialize every editor + new SimSet( EditorPluginSet ); + %count = $editors[count]; + for ( %i = 0; %i < %count; %i++ ) + { + exec( "./" @ $editors[%i] @ "/main.cs" ); + + %initializeFunction = "initialize" @ $editors[%i]; + if( isFunction( %initializeFunction ) ) + call( %initializeFunction ); + } + + // Popuplate the default SimObject icons that + // are used by the various editors. + EditorIconRegistry::loadFromPath( "tools/classIcons/" ); + + // Load up the tools resources. All the editors are initialized at this point, so + // resources can override, redefine, or add functionality. + Tools::LoadResources( $Tools::resourcePath ); + + //Now that we're done loading, we can set the instant group back + popInstantGroup(); + $instantGroup = MissionCleanup; + pushInstantGroup(); + + } + + function startToolTime(%tool) + { + if($toolDataToolCount $= "") + $toolDataToolCount = 0; + + if($toolDataToolEntry[%tool] !$= "true") + { + $toolDataToolEntry[%tool] = "true"; + $toolDataToolList[$toolDataToolCount] = %tool; + $toolDataToolCount++; + $toolDataClickCount[%tool] = 0; + } + + $toolDataStartTime[%tool] = getSimTime(); + $toolDataClickCount[%tool]++; + } + + function endToolTime(%tool) + { + %startTime = 0; + + if($toolDataStartTime[%tool] !$= "") + %startTime = $toolDataStartTime[%tool]; + + if($toolDataTotalTime[%tool] $= "") + $toolDataTotalTime[%tool] = 0; + + $toolDataTotalTime[%tool] += getSimTime() - %startTime; + } + + function dumpToolData() + { + %count = $toolDataToolCount; + for(%i=0; %i<%count; %i++) + { + %tool = $toolDataToolList[%i]; + %totalTime = $toolDataTotalTime[%tool]; + if(%totalTime $= "") + %totalTime = 0; + %clickCount = $toolDataClickCount[%tool]; + echo("---"); + echo("Tool: " @ %tool); + echo("Time (seconds): " @ %totalTime / 1000); + echo("Activated: " @ %clickCount); + echo("---"); + } + } + + // Shutdown. + function onExit() + { + if( EditorGui.isInitialized ) + EditorGui.shutdown(); + + // Free all the icon images in the registry. + EditorIconRegistry::clear(); + + // Save any Layouts we might be using + //GuiFormManager::SaveLayout(LevelBuilder, Default, User); + + %count = $editors[count]; + for (%i = 0; %i < %count; %i++) + { + %destroyFunction = "destroy" @ $editors[%i]; + if( isFunction( %destroyFunction ) ) + call( %destroyFunction ); + } + + // Call Parent. + Parent::onExit(); + + // write out our settings xml file + EditorSettings.write(); + } +}; + +function fastLoadWorldEdit(%val) +{ + if(%val) + { + if(!$Tools::loaded) + { + onStart(); + } + + if(Canvas.getContent() == MainMenuGui.getId()) + { + //startGame(); + activatePackage( "BootEditor" ); + ChooseLevelDlg.launchInEditor = false; + StartGame("tools/levels/BlankRoom.mis", "SinglePlayer"); + } + else + { + toggleEditor(true); + } + } +} + +function fastLoadGUIEdit(%val) +{ + if(%val) + { + if(!$Tools::loaded) + { + onStart(); + } + + toggleGuiEditor(true); + } +} + +function Tools::LoadResources( %path ) +{ + %resourcesPath = %path @ "resources/"; + %resourcesList = getDirectoryList( %resourcesPath ); + + %wordCount = getFieldCount( %resourcesList ); + for( %i = 0; %i < %wordCount; %i++ ) + { + %resource = GetField( %resourcesList, %i ); + if( isFile( %resourcesPath @ %resource @ "/resourceDatabase.cs") ) + ResourceObject::load( %path, %resource ); + } +} + +//----------------------------------------------------------------------------- +// Activate Package. +//----------------------------------------------------------------------------- +activatePackage(Tools); + +//This lets us fast-load the editor from the menu +GlobalActionMap.bind(keyboard, "F11", fastLoadWorldEdit); +GlobalActionMap.bind(keyboard, "F10", fastLoadGUIEdit); + +function EditorIsActive() +{ + return ( isObject(EditorGui) && Canvas.getContent() == EditorGui.getId() ); +} + +function GuiEditorIsActive() +{ + return ( isObject(GuiEditorGui) && Canvas.getContent() == GuiEditorGui.getId() ); +} diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/MaterialToolbar.ed.gui b/Templates/BaseGame/game/tools/materialEditor/gui/MaterialToolbar.ed.gui new file mode 100644 index 000000000..efc140578 --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/gui/MaterialToolbar.ed.gui @@ -0,0 +1,68 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MaterialEditorToolbar) { + canSaveDynamicFields = "0"; + internalName = "ShapeEditorToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "672 0"; + Extent = "802" SPC getWord(EditorGuiToolbar.extent, 1); + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + EdgeSnap = "0"; + text =""; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 7"; + extent = "76 16"; + minExtent = "8 8"; + visible = "1"; + text = "Material Library"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "86 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "91 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "materialSelector.showDialog();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select and Edit an Existing Material"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/materialSelectorIcon"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/Profiles.ed.cs b/Templates/BaseGame/game/tools/materialEditor/gui/Profiles.ed.cs new file mode 100644 index 000000000..fb70def29 --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/gui/Profiles.ed.cs @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Material Editor Written by Dave Calabrese of Gaslight Studios + +singleton GuiControlProfile (GuiMatEdSliderProfile) +{ + bitmap = "./matEdSlider"; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiMatEdRightJustifyProfile) +{ + // font + fontType = "Arial"; + fontSize = 14; + fontCharset = ANSI; + + fontColor = "0 0 0"; + + justify = "right"; + category = "Editor"; +}; + +singleton GuiControlProfile(GuiMatEdPopUpMenuProfile) +{ + opaque = false; + mouseOverSelected = true; + textOffset = "3 3"; + border = 1; + /*borderThickness = 1;*/ + fixedExtent = true; + //bitmap = "./images/scrollbar"; + bitmap = "tools/editorClasses/gui/images/scroll"; + hasBitmapArray = true; + profileForChildren = GuiControlListPopupProfile; + fillColor = "255 0 0 255"; + fontColor = "255 255 255 255"; + fillColorHL = "50 50 50"; + fontColorHL = "220 220 220"; + borderColor = "100 100 108"; + category = "Editor"; +}; + +singleton GuiControlProfile (MatEdCenteredTextProfile) +{ + fontColor = "0 0 0"; + justify = "center"; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b2106c22d0cd92d40ea06ae0e737d6aaf97dbe GIT binary patch literal 759 zcmV<T0to$yP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!nMp)JRCwB?R83D4Q53yXY*{Ha0WqNo zQwzAXEKE!qX}eGpS%`^YOS^|C8{JXLl8vx8q5TEo4=_b{bs=?SN~8=55ffUc+Rmq) z=eaYTN{MmfB&U7%-MJ^{evKrAp#Oa?@Q)-(eV1P&6B*nr0IBL!j7q5lUn{^4P|U5Y z?3{gHVkJJwL1aP>5WR#I?*_?-*Ty&AKYH+Fetw>+)hd<CWikw78@?>aRE`VyY}H6i z{zYQfCPBcoiGQGQ`|ayxRaNQg=qM#;W@u?~ktQc6=MYlLudRjqNK7aI6pJX`hHXFv z&UxR@M2_Q-6bR7l{aK1eqpWtTNAfNb%YYybWSbtrV}M#j&*3W#oz~Sxqd}%+k!hG@ zm?k9>2?~Wmb5N6VNemJTI*Ce)L=LHZhn;~9Rf2#wb}*>gw%sEz0j?#|;_wBxJ36}H zRvD^+tOF%5U)?*Zt@3?-=u*<MZJ7xy3otEL)$s5LyFDekmqPyTQ7wJvs8)cj>gQ&` zvY^B?(3wAKLW6_sl3gVlWIJa9*gpT*w&fC7P{3d%tf)6^x)+msF&0gNtf&bJOMg{j zdcj=+?ikOLOVaixF^t?4r`t9`BD^ss-@x(1&*l#qHF4AM>%l}P-%j||oo-D;5h=u* zBsS5F%dojFJSXT5B@q#}&$O2nwc9Of)K59Q=#o4;Rtg$X1;yQJKm*R30AXg9pB^h| zXJnWhwA3fS*waC6pE{k+Ht$hyVlf*Jp+LsKm6|S*P;i&)y2HU>$R*G;jrKq7k*@0% z-k-j)Uq3!nKrpR&E3n&gyF{6#o3~a61_r2JuhW;$hqPDy$OOEAX@3Fxw%C8NW8)bI pQ;epiE?%FaE2)`(ehPjIFaR=|k5OL7lRy9f002ovPDHLkV1iI$Pfq{< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d803dc6afd1f2b92a9b9cd50610b28e1b12b70b8 GIT binary patch literal 758 zcmV<S0tx+zP)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!m`OxIRCwB?R83D4Q4~Ebw5*hxfSAyP zseHH;7A7W*v|Xr)EX2gHrQJi6jqWIA=|<R_K!1Vw15D8!E(BMmM9QELF`;#;?R?5S z&zlbfV&cM=oSApun|pHJz2{8?=f>CMP(A`o6WQv>AS&6kTvmy7qFkJxUqAi55CV}f z*i8;8QQ##PMuQ|}@9MzP`-k@*PfbmsTCHMZV*{pXu5qtGGA$&D%1JdjsQiN1bRa;C zS$q?#x8A;<(=-iN`udO=AII#>42Fk?Cz(<yEiNWP0Un7(1%zq?xi({yDmX8NA4wwD zbrFe1F>!AKsZ>g;T{$6nmmJKKV3a5{eS${>TEKJeRl1MrTBFf`W!tb!3#MfuolYYj zk55uf)*~??rVWU#7DSOp`OY}cn5qPbH%=_3IgWEeU=gwoJeJI#mXA(@U62zNR3i#5 zl`wpDcfYnE*Gt_OGq&R>lEAhJ%l1_D^z_PYUy0!#p>$`zmb<-QTV<>n=h|-DRAQOz z%pW!Jt}eNycuI82c26b9`227j+as{4Ktd{!io(%p_&LQF{izJe%DO-z@>eC6pBxa# zjs<=~XWMsTg1uJVplt#ovDB|z=k&wR<`0E6@s1JJLlPZ+J4E1OSrbv_5k{L3%k0J_ z#<DKbC+rR~%t$z=+A9^c+buNeM>4<ckvu(6S9M^E#a(Go1Lw;G=$W}E2Wrmk?Sadd zh6J=dc56F0K0aOx?~&(NdgB2VC^TH9=@AK)YsH`>iC8S|5$L*(oln~^3}Z{)pEI_9 zcCe?CU`+R~z-G(q67sV*ZZ33mbf8|Z<ICqgY*#-@0(k**;ROslc6Jo{2l6gm%%){8 oTpPjV?D)Tb1@iH~<F^0<0DIe#LO8tNfdBvi07*qoM6N<$f|qAg9RL6T literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/change-material-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc985e1c84abec9cfa1d40a1316ce1fb8087a52 GIT binary patch literal 668 zcmV;N0%QG&P)<h;3K|Lk000e1NJLTq000pH000pP1^@s6J8eh$0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!K1oDDRCwB)RJ)2IQ4lRf6eBed#Ha<0 z4A#L=&>1i@nGG~C&`Hc>cQ6yA^$T_<F)*3+2Xyue#tn_u4Mh+W!Nf-(zQ)I%)ASYT zWMLZ$>vq*Sb#7I)HnBX7Mx$u6*$Qy)dA?jO{F%Udu(q!2z1w`oZR${~)ii*+u$T$r z`Mb~O`<lz;WH=m3zu%YLZuf@R6UbJI#7?JLt!n4<`8P~Xr_<f>cof)=dcFR<TCF4y z2uLcGl5)8$kx1kbkasBdDgg!wfL+)G_VDrXK>`haLnV=j1)xkanT&)&A*y{<aYv<6 zDFPHj!X1EOx7)v%GQT0_1$ebsEW9q4OAd#FVTnXSrqk&o)Ua<oI|vG}0fIUJOZX(E zDk}U%Ayl{D@2|!_;99IzFq9y{m;#irQBO#qlAq0Hvu;i+g#wx)P-^^szo-d1*hJ>G zS}g-qDwPV>u~P}AawVWSolb@{r9|z*6<~b`x>ce4Yhz82p3mn~AZsJ&<rJ(+tk>bh z%zY9EUT^~moPb1)&6~|;KjHTe`$3TPI<whK%q<p9r;SQ6RC&QPd|Wb|HFG*CnAbX; z^?EJy`JDK+LDK1T2pRzVy4`Lm{lx@5Blh5SyNyLt1XMI0k6GYNeMiQ|g5a}R{hdkh zCwQl;t8o$}*lad38jS?S23!Z#vug*_=`_EeF5DP;GeM6ukH^!9$K!f99G2yBX<UH{ ztRJAwI?iM=Swx@!yAK9~5{*Xx{a0|iBmevS6<`4Jvf3&qnL1AZ0000<MNUMnLSTaC CuPP+~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_cubePreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_cubePreview.max new file mode 100644 index 0000000000000000000000000000000000000000..fa6249ee95ce08e25c8484cdce6700665d0c86ee GIT binary patch literal 253952 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 PcmzBG9s!R)eG&No{V5C~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_previewMat.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cubeMapEd_previewMat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b10e74052baa66c8bd8aa975aa705d0448bd41d9 GIT binary patch literal 306 zcma)$Jqp5541mA9r%*+txr=)d<biq$wN4IB4(=YTLl5E4;sL}f#0XNUF8x9vd?7z} zwmbPE%@Y!!^uQkI?6BD#5qi8O_GCOQ_v<ZX<woqr3F%Jw5*t3_LBzYBO`~gJ6`BzU mQIwz(Rjg*|Z?_rvrMAh2`HunpGj?SZ#WzQ#V3PW)!Y(hF?Hnfn literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_xNeg.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_xNeg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6501386c6bd6549a3b0bf8f22d18f938fc48bda0 GIT binary patch literal 8394 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 bQvBpT=Be9xJJ<zWUbK=ez{8*5-z@wKdG*_& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_xPos.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_xPos.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c01a9c5209663307325c249ac8317147f88bac48 GIT binary patch literal 8907 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_yNeg.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_yNeg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3fdd38db17036a246603bcc641f31c67187ab50 GIT binary patch literal 7870 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_yPos.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_yPos.jpg new file mode 100644 index 0000000000000000000000000000000000000000..539cf776219625e6ec0d13ed5e3ecd251548a321 GIT binary patch literal 8272 zcmeHsWl$XNvi`tgNeIC$B*5Yt7ALrb;J(1J=)yv9hd^*=ac9xs?hsrO+}$O?LxO}P zKmr6V=gRM#D<AHMQ+59z{yjBSGu_p1KixIe{m%2=F5P|t2$kd&<pB�I;~bfZIcW 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_zNeg.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_zNeg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b81b6f920f156ea3f3730051dccb4c43f5d662c0 GIT binary patch literal 7986 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 Yho}hhK~O9?pZ^FyV#R+b_<H7l0DATT#sB~S literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cube_zPos.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/cube_zPos.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7d7d797b040c9dda375412cf0199a242c764d199 GIT binary patch literal 8582 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_d.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce0b477b66a4e0dc7656233b8856bced450f8c2 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1Ghdo^!Ln>~)xfRXTY#`A3aEXWJ(&>9s%wv<kPuiNHdo=E{ zqzpIf{8Xm@2RdI|u32oSYPdAS>FCuLlZx$b{`EHUSa<ICJx?>EKgG%Xa@YRE7ES(g zjxqn_=kIJh1`(gF{&p81I4)4%kooCW&2yOmoy`q?`_^77{{HFwE7ez<xTcB&oq!MM zL|?Z17`2J_@w@VkwGrn_r@VR25Owj<PQFB&4}wW=kLT`V;$d*h)-HGd8*&w7kf*Dk J%Q~loCIBUMZI=K5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_h.png new file mode 100644 index 0000000000000000000000000000000000000000..acea032817d317d4e8b15d28cdc10e745078009a GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GA)YRdAr-gY+}bF}puoZG@Ihwk-|z!&?GOH#WPYyQw91Mt u{r<UyzpIaa04l}{{;*W!Pu)0uw(<c6p(K_**T(5?AoZTEelF{r5}E)J#WFhp literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_i.png b/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_i.png new file mode 100644 index 0000000000000000000000000000000000000000..f2ee385bf34992140548858bf29254973624807a GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G@t!V@Ar-gY+}g<7V8G+*$eudmz(4059=tuxhs46(iLR^t zn?KWXaY60e*D|W}_s=))(~tvd!v_xdHPo-&6eb_~n?-^_#fy`-`rfx~APt_belF{r G5}E*3zco1k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/cubemapBtnBorder_n.png new file mode 100644 index 0000000000000000000000000000000000000000..717f9b4f8c236ebb83743be039419cc2c2387f88 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GA)YRdAr-gY+}bF}puoZG@Zrw>l-QgSj&uH#&F-tOUX@(% v?XAqN{oy?PK*jjL0sn^YVUpE5Etz;2+$OTBXx^QA1*G27)z4*}Q$iB}RM9Vb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemapEd_spherePreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/cubemapEd_spherePreview.max new file mode 100644 index 0000000000000000000000000000000000000000..9425d3b4f3b78a095092957d8174fdece4b1b00c GIT binary patch literal 253952 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cubepreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cubepreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..20472c9cf61524b8a7b6c414b0a7bb823611fcbd GIT binary patch literal 1442 zcmcgsJxgOj6rFsS#3Uy1+r~fO3Ja@cV1gppSO^P(3u@F9Lck!2S|~nyYeldU)E{7> zR#Pfs>xwMe3S!|85aKyAZ_FepvD%xQcR%Ldb7$UgU!T|zQvo3cfN=nS4X6)I`!iv# z6<ZfVAZUZoFHbV}TI$c)&|dkUygqqU1Y^+j<zD$u&T>>CT%)Ftk8t~$m1>p!!axh) zN(7GOV>}9Yhn1|i`7^uob}wV_TY)%$6{!^-2_Oly*+5O_tjYD1@1&t^AMWXL_pdsi zyj5!tcM7(=n7oy%4|hJlH-uiNJWC(I+_Q$<im6kq^KI$mXm0Y68+|mlZ!15}92>9W z=e}(1SDa3~j^Evn%j3lB`Z{r!$2}MG?S9;Se(>-enY*dok9$`&o9i~mwKYKl$Nycw zdOkiLKG^5ItMAx$$F=h?V$b%K?Wy8T@2RP6W;f$%>|uQH;|$-iJ!SHAZd03c3Yq&1 z=KjsBFewAH108fQbwLplVN8Am)4g@X*!FY`Qys??Co#b(!RZX^UkuMAkO8^?^F0Zn zmJCtUurh%fR;JM-gI=AY2ie`CS9`i|AQ%d_M53|Qcp}-BO8<ZqA%l?L5Dzi~$+RyP y%K4?@FEO&bt{-OG0e&pH7Ao_FiF|2nakjKlSY0ZtP2|g?JOIRg*@V7zPrd_<sBZ`W literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cylinderpreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_cylinderpreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..75a35933414bc0fd44786641f388cf843bbe1711 GIT binary patch literal 3618 zcmd^CeN2^Q6n_LoL<B?>5GBPA6a}tc`~u;*251JT35nYBgMiYUfhgqW;-a*<G~3#& z5G|ADKQOt>8ajhl&1Kd#OszF-+Ok=G%oLnjo6_(1_?~h9u>V^<?A-G^zvn#XocBDJ zmqV1=q^kUt3WO|z^iq!n%3$h$TJ)7A=MX~q2?@RTokUkbWr)j%Qs2LR%K8DaFZ)Xi zSOaTdEit;W=ugeXcog4!k9WGenXfmb55#u`z>s+4`$Ak(_oum+cO2;&f5Hlc-47B3 zQOd22!bg8dFl2xbJ=!I3JwS+UghERm)}$kkSrl$VYf9z5EEjFA%6%s^pQXT9Js<QF z>smvrE^4}!LHp3u-TKDEBC~fLnl||r*ry+H)2_D<%K74^TkXHw{I!QgmiR+ku#G>? zS&QxXIA`T_`hUhazk#p9dE!sue-G!0e}Zm<O*{kpH{nNISo6gco+o|}@qV>=|3^i- z+4et<dOPemw@}wmJHFJ*vx3<td3Ik+AL#>m!Q`bU<OP$L{*V{!%ll&bNDqB6eWah{ z1(TQFk{3)~`b=K1@wa<!{B2BL`cGakdASSng2~IhkQYo|?ufi#@^W9~1(TP%BQKb| z+#`9x<mFDu3nnl3OI|Q}xoh%*$;%?|d)L0-0l8oL$ej|)Jreugm)whqX@BR5rT>Qa z>A&=uy!4h>`bjK3B$mDrORtEfKg7}#V(A01JUg-dJFn3LoprgI?*SdTXQSTyz>`s2 z@v`<V&CggcK1bg!K9PQWOT2cy=$%O)b=xnMKD57u&(GMu3pol*?hWjzPQO&fT>zVB zA7G!c&cmk!vA4n}3c2uJG`YWmPXz4e;j;|;uRvx4M}up{9^RG^z88&lp7!R_7Oi*H zEityOb!lm-v2CrL&I)7OTA%UewlSWqom>8lb26T-^;vIjI&E9)(_XK!ZLRC;qVYa- zJ8i8yyt&4<HG1hbwypK<x;kUqT2FHxG`6kvcV4ftZLMD|y=rV*Yy4d?_1hYC2OHbg zdQV-nv2CqamyR{It&z`ZY+C~}UeA5Y{r~&!9aWm;wgjQ?{|kKalWP4|XJPv4=_4G( z5AGDUUG6xUydkejhvwI2+?-XO9CB~1PHk+>SX|MN{L9fr+O@d9-Zr-}Y50}d`gGDr zJ^F+99p6C5!rpdeyF+9D3jDf~HamEZ=iz^V@fdeWMMJYM_Vvj`Evy51n`=7@+b#eH zIQu!m^K08HaJFVkwj-^vwS53$ZqQkikjrAOotMHVp$3aJ5kp(7iT8<pD;+f@H2Srm z7HbM>BCgD<vVu@k+0~d9<g~^icjARp&BO<gJ2Y*&2RW@6<nFBB-yDpybC7$-hr>LV z5HlOO6Fz>oc?fcyNAACNoJ>9jJqC7oRH7pV``6*O`%q|09L`1H{N6{YEnN$1+czR^ zXiR*|BJ`X2wh#L#xw@+`9nWipeeQ5fMqRJLXXgvYlb532K%8A&HrF!`&+#K-#?0U8 zxrpZ*h+I+Ks%0Xc`!I4REL-CF1N}%q?oDq-I|idqgOK}d`C$ig>O08&b;%8f&-dK) z=&{XrpRE5b=Io5M!dEtt!IBv@40s?UoFt}QKjp7_W8w}lQ#HBn>Of4>L71L{F*%1Q zPQ~N!GfL%vF94GHJpwy}AdJPBj7@LO)10|EdvgZoT+aENb2?{t&hwn{S+75$MXJGS zh{{muYNkq4scMFruBNG}YKlrxPBmFgQVx}@l2oFas3xccH6As@tFdZ~8jY&r)JXM| z8i5#*DoRDGp(;iVQ^QrPnuXW23-UB16OskVhRlY{fjk453waij1IdNtLFPf`Ll!`u zgDiyPBU%`q63;q9_44!Y-6x=LV85XL!2?1<Cp>9I6vW1QJk4>V-0o5CbLZmA%bQ#c z4a@G{J#r)^C9=N$(8QV=z4iY6GiTO3X@w0eE?K{F)tbpFbK{y-Yl}<P^&k{szR$h* o&q~Bg8;VNuR+eQKFE3kHQn{+6DsScbOl)w;e^z+w<AhuP4eUiiWB>pF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_spherepreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/cubemaped_spherepreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..ed117d33436e7f3e360d251516575019fa591e99 GIT binary patch literal 22082 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubematEd_cylinderPreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/cubematEd_cylinderPreview.max new file mode 100644 index 0000000000000000000000000000000000000000..2926befd6517f6af1af23a250bfbbfb50e02c947 GIT binary patch literal 258048 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 XN5CWC5%36j1Uv#B0gu3uj==v1E_G6E literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cubepreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/cubepreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..18724762de2410f030875b1922e7ad71610b7b8e GIT binary patch literal 1437 zcmc&!%Syvg5S{dyzSH{ZH~84-2c#$kH|`4V)IhrsTG~aCE(H4ruH1{?Aw&=ap<Df( zdd^KIUP{1r<+LX=bLMgHO)_cAy<9pX`@lJXzX4RDX{QNuRkSV(BBLbq^W#yccUSLN zlzh!T!*AfF`w_zSVf(Os?XiR~pPIscguPxASuVL=4#)$x6yRt-^2H=LKY3d$US0Sl zpltZLui*-eDo|76S9jE8ydFCZXkCYU8n@m<3qEWEt)=E!+JKeE8qKAxzO9v4&bMl4 zE3aBP`pT(ZjivF-S%v1Q);N8eQ(Ltk_fnr)!I;{r$@#oTB78Q#&nMlsb8Oo*Sn2T} zadVDPcY`ymV<+!q`k777IA^VX@tS|#OS`Y`c$1n<_9DVLh1{eedj8R@Fv$a&K#LBh zE+bjVVe$*fbT1!VOm$f*nBf{`l+&3rKNr_3-~rn}^gYR<mK}<yVPyq1tZZNn4{L47 zF0yxI&v-3)a>U7GbNNEiEtM<PTD@@q8A1*rJ;)q#1@W5w!8EuX9?R)?W-igT9eykt aqhQ+YUyp*xWYE6|re{14L_ca0zw|#A%Lk|c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/cylinderpreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/cylinderpreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..dfe5ec1bccaf70381dedf1457deedc3abc3cacc1 GIT binary patch literal 3613 zcmd^Ce@vBS6n+IoL<B?>5GBPQC<<J?_zQ&h8lV}VCM0T0g#o3hK_KMj;-a*<G~3#& z5G|ADKQMt=Luc@*xy-tTskNp}TQ<udGX<yCru00Y?-}<G`@hx8&OOh0-t(SwzH=@g zFA-{!s`6FJAF>3}M?DrO8&m(&qOUADhY-p~Na(%q#Jln;gIr#e`u_D()(?<<nO|DK z8dw8siP3{ae`+qqqj=|gywlUee0?GPAl}OlhQuS^AL5$6KgGSW^GNr^6P7>h0gwQQ zQf_T4d<=vHLIw%Zt6c&s0D_DW3@v$BlMX*-QMe7QDV6&&T(r3=_npvumOR_){h*&% z*BaV%VbiTN+J~m^);As&+I!cbX_H@tefkkM?Rxv5oG)s+)$zOGuRScZ#2?y%ZTsV# zwbaDNIV-En_cO-%4SW^O6MqW-dpJ-06LdRl;#t_g2|wcenlGmDJn?&o_p9N39~J6m z<9{6WcA7Z1P}gu1U+U#q!R(VfyEmqf^ntu!^3oIXg2_vN$P4!7y)k{Hhu)Yz(ogb& z$xCm^3nnjpCNJ3bH$Aug4JI%BCoh=1%!0gN@-i3lg2~H_$O|Sf^CB;pyv&ZgVDd6Y z@`B0BOvwu-FY_fYn7quIykPRO$a~M)`#T`>rH{;%SmsFVJujJy9n*a0iKYKG@6~_l zGkNJPvGkKzdPppNBbHtfOMi%^C&bbRVtICA^E<E61D$cXn(qM}zGtJ}{GcVRsQe}E zS)QA=XkwPWU34P#_?B4hdeJkRKI*nlGJR-&3!k5{e;0BTnA{uKQ(ZpEjJpUn&pyCD zW1WXjF=B6pPXuz|y=dqD3O-@5pNG#1?7s||3mgfq4SRT7Lik>^wez$mo3?1Zt8STX z8?8%9N^IL`?R1vgw$b{GC%c{TjCOAMGsekyM(eYl>{QxD>(d^OZ5yrY>mu<!bUTgK zot|vlHX6Nj+qThqcU_%r8?9$J58AfT`a6%uwvE=Wlw7rKqc#4n*!3HYx&v+7XuYQ{ z(zcD(Yf8r3w$aGvv~8n-8LxNVGXH<y+)<^y+?F8p{eOWkep0R9>dH@DGjo)K_`#k0 z_RF0o6F1~k>EPVjw3~Cv5`*ro*U61-X-mr+5`Q_mM7x#_)Z6AaCXBo?SD#K8t;c@w zzT+F{XxQ7XY<FnvUx8nD!e$50@jUzwFdpMBD{pA_#@;^ZsD*VPZ*y%|e)|PrKj#2P zXl`vsInLH>$#kSNwsj0b%ndqY3UXP@wewQw6x3j`CSqudHF2KUw^C74T%%7bYO$uF zCgRGRDk}grm0pc%MNVr1a>rjd)l7T<xr0+?wjie!h1^~B`<nxCb{=x?_;5taCB)1` z?zoTNZ61bP=aKub9VZiyL63tS8WHbE!v1ym?LHLT8iR9TIKTH%a%=bE+K!Eg8ypqe zx&-}ZzU?DEO04eAPsQ_EA)h-OQ&HEe@Y(so@x<k**B@usl+JHifamxTF{2jlY`KW% z8;o2LJ*sswp8GIz$E{e_@(216huoXqjC2e|pN1g!*|Nh9<kWYN`|IKx4zKUI-J{1g z-+i+FyO?)ptX1B!i42z9QA2<SLqbX7w(FyORbSk={p?#cxt?l&+@=F?dk)0SIY{wV zJOMvrR2KL=Ai2MXVP^=0u^5xF>CO8z@7%n5^A65?Iq&DZr}OU4`#kUXtk)OO!qreU zOr@z*HCv^qWHn38R5R3cHBBWcr<$s!D2GZ^2`XMqR+Chmnur==)p#{djYU;4YP5Pv zjY5oY6`>;4a22ISsF5mK&B1Hh1$i2h4#|LILgqr|L7sukhdc|(f@DK-APXQ1A&Vf- zK^8-D5iJByY4@#<k8j_8e*OIi1Plxu6cjw^Nh>TbI=ZE$IcAL8J;r_RTx?lcldGX& z#ofC{jwB_8*ViAKTvMaB-oJn5%-ScdkikX86$Pu;PF3j}*REb)RQy^mGQ(4S_|Ho4 gx`K+#qLu3kN=u82atbQa!EwnyRe0g!%kS2I1AzBG`Tzg` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/gridTiny2.PNG b/Templates/BaseGame/game/tools/materialEditor/gui/gridTiny2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..6ce109308bb289cdcee0bb63f5bcb4d3f6f38b3f GIT binary patch literal 3017 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 JPA$|a`oCCPSUvy% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPreviewWindow.ed.gui b/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPreviewWindow.ed.gui new file mode 100644 index 000000000..8110d2c34 --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPreviewWindow.ed.gui @@ -0,0 +1,796 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + internalName = "MatEdPreviewWindowContainer"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(MaterialEditorPreviewWindow) { + canSaveDynamicFields = "0"; + internalName = "MatEdPreviewWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Extent = "210 251 "; + MinExtent = "210 150"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1)-1; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + closeCommand = "MaterialEditorPreviewWindow.setVisible(false);"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Material Preview"; + + /*new GuiContainer(MaterialEditorPreviewPane) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; //1 + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "4 23"; + Extent = "200 221"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0";*/ + + new GuiContainer(matEd_previewPanel) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "4 45"; + Extent = "202 202"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "Client"; + Margin = "24 1 3 3 "; + + + new GuiSwatchButtonCtrl(matEd_previewBackground) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "-1 -1"; + Extent = "204 204"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + color = "0 0 0 .8"; + //bitmap = "tools/materialEditor/gui/gridTiny2.PNG"; + //wrap = "1"; + }; + new GuiContainer(){ // this is blocking the mouse imput to the swatch imput behind it + HorizSizing = "width"; + VertSizing = "height"; + Position = "-1 -1"; + Extent = "204 204"; + }; + new GuiMaterialPreview(matEd_previewObjectView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "1 1"; + Extent = "199 199"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + }; + //}; + }; + + new GuiPopUpMenuCtrl(matEd_quickPreview_Popup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 24"; + Extent = "67 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updatePreviewObject();"; + ToolTip = "Changes the Preview Mesh"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Sphere"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiSwatchButtonCtrl(MaterialPreviewBackgroundPicker) { // Background Color + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "189 229"; + Extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF($thisControl.color, \"MaterialEditorGui.updatePreviewBackground\");"; + color = "0 0 0 .8"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + // Ambient light color picker + new GuiSwatchButtonCtrl(matEd_ambientLightColorPicker) { + canSaveDynamicFields = "0"; + Enabled = "1"; + color = "1 1 1 1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "81 28"; + Extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF($ThisControl.color, \"MaterialEditorGui.updateAmbientColor\");"; + hovertime = "1000"; + groupNum = "-1"; + ToolTip ="Change Ambient Light Color"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + // Light color picker + new GuiSwatchButtonCtrl(matEd_lightColorPicker) { + canSaveDynamicFields = "0"; + Enabled = "1"; + color = "1 1 1 1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "75 23"; + Extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF($ThisControl.color, \"MaterialEditorGui.updateLightColor\");"; + hovertime = "1000"; + groupNum = "-1"; + ToolTip ="Change Normal Light Color"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiCheckboxCtrl(){ + position = "108 25"; + Extent = "98 18"; + HorizSizing = "left"; + profile = "ToolsGuiCheckBoxProfile"; + Variable = "MaterialEditorGui.livePreview"; + Command = "MaterialEditorGui.updateLivePreview($ThisControl.getValue());"; + text ="Preview in World"; + }; + }; + new GuiWindowCtrl(matEd_cubemapEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "200 257"; + Extent = "478 248"; + MinExtent = "478 248"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + closeCommand = "MaterialEditorGui.hideCubemapEditor(true);"; + text = "Cubemap Editor"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextProfile"; + position = "307 40"; + Extent = "30 16"; + text = "Name"; + }; + new GuiTextEditCtrl(matEd_cubemapEd_activeCubemapNameTxt) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "338 40"; + Extent = "131 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "myCubemap 1"; + maxLength = "1024"; + AltCommand = "MaterialEditorGui.editCubemapName($ThisControl.getText());"; + }; + new GuiButtonCtrl(){ + Profile = "ToolsGuiButtonProfile"; + position = "339 216"; + Extent = "74 24"; + text = "Select"; + command = "MaterialEditorGui.selectCubemap();"; // needs hookup use selected cubemap + }; + new GuiButtonCtrl(){ + Profile = "ToolsGuiButtonProfile"; + position = "417 216"; + Extent = "52 24"; + text = "Cancel"; + command = "MaterialEditorGui.hideCubemapEditor(true);"; // needs hookup Cancel + }; + new GuiScrollCtrl(matEd_cubemapEd_availableCubemapScroller) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 40"; + Extent = "154 203"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiListBoxCtrl(matEd_cubemapEd_availableCubemapList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiListBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "128 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + AllowMultipleSelections = "0"; + fitParentWidth = "1"; + }; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextProfile"; + position = "6 22"; + Extent = "67 16"; + text = "Cubemaps"; + }; + // ------------------------------ Right X Positive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_XPos) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "299 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_xPosTxt) { + position = "304 110"; + Extent = "57 10"; + text = "+ X Right"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateXPOSImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "299 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"0\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + // ------------------------------ X Negitive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_XNeg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "167 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_xNegTxt) { + position = "171 110"; + Extent = "57 10"; + text = "- X Left"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateXNEGImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "167 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"1\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + // ------------------------------ Y Positive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_YPos) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 172"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_yPosTxt) { + position = "237 175"; + Extent = "57 10"; + text = "+ Y Front"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateYPOSImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 172"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"3\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + // ------------------------------ Y Negitive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_YNeG) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 40"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_yNegTxt) { + position = "237 44"; + Extent = "57 10"; + text = "- Y Back"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateYNegImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 40"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"2\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + // ------------------------------ Z Positive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_ZPos) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_zPosTxt) { + position = "237 110"; + Extent = "57 10"; + text = "+ Z Top"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateZPosImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "233 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"4\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + // ------------------------------ Z Negitive ------------------------------------ + new GuiBitmapCtrl(matEd_cubemapEd_ZNeg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "365 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl(matEd_cubeMapEd_zNegTxt) { + position = "369 110"; + Extent = "57 10"; + text = "- Z Bottom"; + }; + new GuiBitmapButtonCtrl(matEd_cubeMapEd_updateZNegImg) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "365 106"; + Extent = "64 64"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.editCubemapImage(\"5\", $ThisControl.bitmap );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When using Static Cubemaps, select your CubeMap by clicking here."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + + // Create New Cubemap + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "128 23"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "matEd_addCubemapWindow.setVisible(1);"; // -------------- Needs Hookup Create New Cubemap + hovertime = "1000"; + tooltip = "Create New Cubemap"; + bitmap = "tools/gui/images/new"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "143 23"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.showDeleteCubemapDialog();"; // -------------- Needs Hookup Delete Cubemap + hovertime = "1000"; + tooltip = "Delete Cubemap"; + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + internalName = "saveCubemap"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "top"; + position = "106 23"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.showSaveCubemapDialog();"; // -------------- Needs Hookup Save Cubemap + hovertime = "1000"; + tooltip = "Save Cubemap"; + bitmap = "tools/gui/images/save-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + + new GuiWindowCtrl(matEd_addCubemapWindow) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "362 333"; + Extent = "300 99"; + MinExtent = "48 92"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Create Cubemap"; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "cubemapName"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "96 35"; + Extent = "196 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + AltCommand = ""; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "12 36"; + Extent = "77 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + text = "Cubemap Name"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "96 68"; + Extent = "126 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + text = "Create"; + Command = "MaterialEditorGui.addCubemap( matEd_addCubemapWindow-->cubemapName.getText() );matEd_addCubemapWindow.setVisible(0);"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "228 68"; + Extent = "64 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + text = "Cancel"; + Command = "matEd_addCubemapWindow.setVisible(0);"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPropertiesWindow.ed.gui b/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPropertiesWindow.ed.gui new file mode 100644 index 000000000..328d946b6 --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/gui/guiMaterialPropertiesWindow.ed.gui @@ -0,0 +1,4091 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MaterialEditorGui,EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "MatEdPropertiesWindowContainer"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(MaterialEditorPropertiesWindow) { + canSaveDynamicFields = "0"; + internalName = "MatEdPropertiesWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Extent = "210 446"; + MinExtent = "210 316"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(MaterialEditorPreviewWindow.extent, 1) - 2; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + closeCommand = "MaterialEditorPropertiesWindow.setVisible(false);"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Material Properties"; + + new GuiContainer(){ // Client group + isContainer = "1"; + Docking = "Client"; + Margin = "3 1 3 3"; + Position = "4 24"; + Extent = "202 668"; + + new GuiContainer(){ // container to prevent transparent collapsing from effecting children. + Position = "0 21"; + Extent = "202 39"; + isContainer = "1"; + HorizSizing = "width"; + Visible = "1"; + + new GuiContainer(MatEdMaterialMode){ // Edit Mode + Position = "0 0"; + Extent = "202 39"; + isContainer = "1"; + HorizSizing = "width"; + Visible = "0"; + + new GuiTextCtrl(){ + Position = "1 1"; + Extent = "39 16"; + Profile = "ToolsGuiTextRightProfile"; + text = "Material"; + }; + new GuiTextEditCtrl(){ + internalName = "selMaterialName"; + Profile = "ToolsGuiTextEditProfile"; + AltCommand = "MaterialEditorGui.setMaterialDirty();MaterialEditorGui.updateActiveMaterialName($ThisControl.getText());"; // needs hookup + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "45 0"; + Extent = "158 18"; + text = ""; + HorizSizing = "width"; + }; + new GuiTextCtrl(){ + Position = "1 21"; + Extent = "39 16"; + Profile = "ToolsGuiTextRightProfile"; + text = "Target"; + }; + new GuiTextCtrl(){ // mesh name should not include the path + internalName = "selMaterialMapTo"; // will use the first child found with that name if called from a previous parent even if it is invisable. + Position = "46 21"; + Extent = "141 16"; + HorizSizing = "width"; + VertSizing = "bottom"; + text = ""; + }; + }; + new GuiContainer(MatEdTargetMode){ // Selection Mode + Position = "0 0"; + Extent = "202 39"; + isContainer = "1"; + HorizSizing = "width"; + Visible = "1"; + + new GuiBitmapButtonCtrl(){ + Profile = "ToolsGuiButtonProfile"; + Position = "186 23"; + Extent = "17 17"; + HorizSizing = "left"; + tooltip = "Swap material on the object with existing"; + bitmap = "tools/materialEditor/gui/change-material-btn"; + command = "materialSelector.showDialog(\"MaterialEditorGui.showMaterialChangeSaveDialog\");"; + }; + + new GuiTextEditCtrl(){ + internalName = "selMaterialName"; + Profile = "ToolsGuiTextEditProfile"; + AltCommand = "MaterialEditorGui.setMaterialDirty();MaterialEditorGui.updateActiveMaterialName($ThisControl.getText());"; // needs hookup + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "76 21"; + Extent = "107 18"; + text = "myMaterial 1"; + HorizSizing = "width"; + }; + new GuiTextCtrl(){ // mesh name should not include the path + internalName = "selMaterialMapTo"; + Profile = "ToolsGuiTextRightProfile"; + Position = "1 1"; + Extent = "70 16"; + HorizSizing = "right"; + VertSizing = "bottom"; + text = ""; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + Position = "1 21"; + Extent = "70 16"; + HorizSizing = "right"; + VertSizing = "bottom"; + text = "Material"; + }; + new GuiPopupMenuCtrlEx(SubMaterialSelector){ // needs hookup will show the name of the current mesh Maped to + Profile = "ToolsGuiPopUpMenuProfile"; + Position = "76 0"; + Extent = "126 17"; + HorizSizing = "width"; + VertSizing = "bottom"; + text = ""; + tooltip = "Target Material"; + Command = "SubMaterialSelector.onSelect();"; + reverseTextList = "0"; + }; + }; + }; + + // make this shorter //////////////////////////////////////////////////////////////////////////// + new GuiScrollCtrl(matEd_scrollControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; //height + position = "0 65"; + Extent = "202 603"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl(MatEd_scrollContents) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "187 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiPopUpMenuCtrl() { + canSaveDynamicFields = "0"; + internalName = "MaterialLayerCtrl"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuTabProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "112 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.changeLayer( $ThisControl.getText() );"; + ToolTip = "Changes the material layer being edited"; + hovertime = "1000"; + text = "Layer 0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "185 0"; + Caption = "Basic Texture Maps"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "185 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // Diffuse Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 21"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "diffuseMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"diffuse\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the Active Diffuse Map for this layer"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Diffuse Map"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "diffuseMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "134 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiSwatchButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "colorTintSwatch"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "55 33"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF(materialEd_PreviewMaterial.diffuseColor[MaterialEditorGui.currentLayer], \"MaterialEditorGui.updateColorMultiply\");"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl(){ + profile="ToolsGuiDefaultProfile"; + text = "Color"; + position = "74 34"; + Extent = "30 15"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command = "MaterialEditorGui.updateTextureMap(\"diffuse\", 1);"; + + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"diffuse\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 75"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + new GuiContainer(){ // Normal Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 79"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "normalMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Normal Map"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"normal\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Normal Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "normalMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command = "MaterialEditorGui.updateTextureMap(\"normal\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"normal\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 360"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + new GuiContainer(){ // spec Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 364"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "specMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Spec Map"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateSpecMap(1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Tone Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "specMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command="MaterialEditorGui.updateSpecMap(1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateSpecMap(0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + }; + }; + new GuiRolloutCtrl(advancedTextureMapsRollout) { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "185 0"; + Caption = "Advanced Texture Maps"; + Expanded = false; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "185 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // Detail Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 193"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "detailMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"detail\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Detail Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + + new GuiTextCtrl() { // Detailmap Scale text + profile="ToolsGuiDefaultProfile"; + position = "56 34"; + Extent = "29 16"; + text ="Scale"; + }; + + new GuiTextEditCtrl() { // Detailmap Scale + profile="ToolsGuiNumericTextEditProfile"; + internalName = "detailScaleTextEdit"; + position = "87 33"; + Extent = "28 18"; + text ="0"; + maxLength = "2"; + AltCommand = "MaterialEditorGui.updateDetailScale($ThisControl.getText());"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Detail Map"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "detailMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command = "MaterialEditorGui.updateTextureMap(\"detail\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"detail\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 246"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + + new GuiContainer(){ // Detail Normal Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 136"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "detailNormalMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"detailNormal\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active DetailNormal Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + + new GuiTextCtrl() { // Detail Normal Map Strength text + profile="ToolsGuiDefaultProfile"; + position = "56 34"; + Extent = "29 16"; + text ="Strength"; + }; + + new GuiTextEditCtrl() { // Detail Normal Map Strength + profile="ToolsGuiNumericTextEditProfile"; + internalName = "detailNormalStrengthTextEdit"; + position = "87 33"; + Extent = "28 18"; + text ="0"; + maxLength = "3"; + AltCommand = "MaterialEditorGui.updateDetailNormalStrength($ThisControl.getText());"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Detail Normal Map"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "detailNormalMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command = "MaterialEditorGui.updateTextureMap(\"detailNormal\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"detailNormal\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 189"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + + new GuiContainer(){ // Overlay Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 136"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "overlayMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"overlay\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Overlay Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Overlay Map"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "overlayMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command = "MaterialEditorGui.updateTextureMap(\"overlay\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"overlay\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 189"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + new GuiContainer(){ // light Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 250"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "lightMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Light Map"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"light\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active light Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "lightMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command="MaterialEditorGui.updateTextureMap(\"light\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"light\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiBitmapCtrl(){ + position="6 303"; + extent ="175 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + new GuiContainer(){ // tone Map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "6 307"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + internalName = "toneMapDisplayBitmap"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Tone Map"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"tone\", 1);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Tone Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "toneMapNameText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "143 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "134 34"; + Extent = "40 16"; + buttonType = "PushButton"; + command="MaterialEditorGui.updateTextureMap(\"tone\", 1);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "177 34"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateTextureMap(\"tone\", 0);"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "195 0"; + Caption = "Accumulation Properties"; + Expanded = false; + Margin = "-1 0 0 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "195 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // enable/disable + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 7"; + Extent = "57 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateAccuCheckbox($ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Enables the use of Pixel Specular for this layer."; + hovertime = "1000"; + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + + new GuiContainer(){ // scale + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Scale"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuScaleSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"accuScale[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuScale[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "GuiDefaultProfile"; + ToolTip = "Sets the scale of the accu map."; + hovertime = "1000"; + range = "0.03125 32"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuScaleTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuScale[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1"; + maxLength = "3"; + }; + }; + }; + + new GuiContainer(){ // direction + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Direction"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuDirectionSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"accuDirection[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuDirection[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "GuiDefaultProfile"; + ToolTip = "Sets the direction of the accu map."; + hovertime = "1000"; + range = "-1 1"; + ticks = "0"; + value = "-1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuDirectionTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuDirection[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "-1"; + maxLength = "3"; + }; + }; + }; + new GuiContainer(){ // strength + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Strength"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuStrengthSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"accuStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "GuiDefaultProfile"; + ToolTip = "Sets the strength of the accu map."; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0.6"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuStrengthTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0.6"; + maxLength = "3"; + }; + }; + }; + new GuiContainer(){ // coverage + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Coverage"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuCoverageSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"accuCoverage[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuCoverage[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "GuiDefaultProfile"; + ToolTip = "Sets the coverage of the accu map."; + hovertime = "1000"; + range = "0 2"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuCoverageTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuCoverage[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1"; + maxLength = "3"; + }; + }; + }; + new GuiContainer(){ // specular + profile="GuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "195 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Specular scale"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuSpecularSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"accuSpecular[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuSpecular[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "GuiDefaultProfile"; + ToolTip = "Sets the specular scale over the accu map."; + hovertime = "1000"; + range = "0 2"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "accuSpecularTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"accuSpecular[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1"; + maxLength = "3"; + }; + }; + }; + + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "185 0"; + Caption = "Lighting Properties"; + Expanded = false; + Margin = "-1 0 0 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "185 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // specular + profile = "ToolsGuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "185 44"; + HorizSizing = "width"; + + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "pixelSpecularCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 4"; + Extent = "57 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateSpecularCheckbox($ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Enables the use of Pixel Specular for this layer."; + hovertime = "1000"; + text = "Specular"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiSwatchButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "specularColorSwatch"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "69 4"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF(materialEd_PreviewMaterial.specular[MaterialEditorGui.currentLayer], \"MaterialEditorGui.updateSpecular\");"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + position = "9 26"; + Extent = "72 16"; + text = "Spec strength"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "91 4"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "specularPowerSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"specularPower[\" @ MaterialEditorGui.currentLayer @ \"]\", mCeil($ThisControl.getValue()), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"specularPower[\" @ MaterialEditorGui.currentLayer @ \"]\", mCeil($ThisControl.getValue()), true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Sets the hardness of the Pixel Specular value."; + hovertime = "1000"; + range = "1 128"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "specularPowerTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"specularPower[\" @ MaterialEditorGui.currentLayer @ \"]\", mCeil($ThisControl.getValue()));"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "32"; + maxLength = "3"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "91 26"; + Extent = "96 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "specularStrengthSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "61 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"specularStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"specularStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue(), true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Sets the strength of the Pixel Specular value."; + hovertime = "1000"; + range = "0 5"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "specularStrengthTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "64 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"specularStrength[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1"; + maxLength = "3"; + }; + }; + }; + new GuiContainer(){ // glow emissive + profile = "ToolsGuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "185 22"; + HorizSizing = "width"; + + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "glowCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "70 4"; + Extent = "40 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"glow[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Determines if this layer will Glow or not."; + hovertime = "1000"; + text = "Glow"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "emissiveCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 4"; + Extent = "60 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"emissive[\" @ MaterialEditorGui.currentLayer @ \"]\",$ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Emissive causes an object to not be affected by lights. Good for light sources."; + hovertime = "1000"; + text = "Emissive"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiContainer(){ // parallax + profile = "ToolsGuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "185 24"; + HorizSizing = "width"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 3"; + Extent = "54 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Parallax"; + maxLength = "1024"; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "70 3"; + Extent = "115 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "parallaxSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "82 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"parallaxScale[\" @ MaterialEditorGui.currentLayer @ \"]\",$ThisControl.getValue(), true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateActiveMaterial(\"parallaxScale[\" @ MaterialEditorGui.currentLayer @ \"]\",$ThisControl.getValue(), true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Parallax Scale"; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "parallaxTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "85 0"; + Extent = "29 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateActiveMaterial(\"parallaxScale[\" @ MaterialEditorGui.currentLayer @ \"]\",$ThisControl.getValue());"; + hovertime = "1000"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0"; + maxLength = "3"; + }; + }; + }; + new GuiContainer(){ + profile = "ToolsGuiTransparentProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "185 84"; + HorizSizing = "width"; + + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "useAnisoCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 4"; + Extent = "108 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"useAnisotropic[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Enables the use of anisotropic filtering for this layer."; + hovertime = "1000"; + text = "Anisotropic filtering"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "vertLitCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 25"; + Extent = "102 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"vertLit[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Enables the use of vertex lighting for this layer."; + hovertime = "1000"; + text = "Vertex lit"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "vertLitCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "113 25"; + Extent = "102 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"vertColor[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Multiply vertex colors with diffuse colors for this layer."; + hovertime = "1000"; + text = "Vertex colors"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "subSurfaceCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 46"; + Extent = "79 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"subSurface[\" @ MaterialEditorGui.currentLayer @ \"]\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Enables the use of subsurface scattering for this layer."; + hovertime = "1000"; + text = "Sub Surface"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + }; + }; + new GuiRolloutCtrl(materialAnimationPropertiesRollout) { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "185 0"; + Caption = "Animation Properties"; + Expanded = false; + Margin = "-1 0 0 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "185 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // Rotation Animation Properties + profile="inspectorStyleRolloutInnerProfile"; + isContainer = "1"; + position = "-1 96"; + Extent = "185 94"; + HorizSizing = "width"; + + new GuiCheckboxCtrl() { + canSaveDynamicFields = "0"; + internalName = "RotationAnimation"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiInspectorCheckBoxTitleProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 -1"; + Extent = "112 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateAnimationFlags();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Rotation Animation"; + maxLength = "1024"; + }; + + new GuiControl(){ + class = "AggregateControl"; + position = "0 29"; + Extent = "135 20"; + + new GuiTextCtrl(){ // u + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 1"; + Extent = "12 16"; + text = "U"; + }; + + new GuiSliderCtrl() { // u + Profile = "ToolsGuiSliderProfile"; + internalName = "RotationSliderU"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "25 2"; + Extent = "68 15"; + Command = "MaterialEditorGui.updateRotationOffset(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateRotationOffset(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change U Scroll Direction"; + hovertime = "1000"; + range = "-1 0"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl(){ // u + internalName = "RotationTextEditU"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 0"; + Extent = "34 18"; + text = "0"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl);"; + }; + }; + + + + new GuiControl() { + class = "AggregateControl"; + position = "0 50"; + Extent = "135 20"; + + new GuiTextCtrl(){ // v + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 1"; + Extent = "12 16"; + text = "V"; + }; + + new GuiSliderCtrl() { // v + Profile = "ToolsGuiSliderProfile"; + internalName = "RotationSliderV"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "25 2"; + Extent = "68 15"; + Command = "MaterialEditorGui.updateRotationOffset(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateRotationOffset(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change V Scroll Direction"; + hovertime = "1000"; + range = "-1 0"; + ticks = "0"; + value = "0"; + }; + + new GuiTextEditCtrl(){ // v + internalName = "RotationTextEditV"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 0"; + Extent = "34 18"; + text = "0"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateRotationOffset();"; + }; + }; + new GuiTextCtrl(){ // Pivot Point + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 16"; + Extent = "34 16"; + text = "Pivot"; + }; + new GuiBitmapCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + position = "136 20"; + Extent = "48 48"; + isContainer = true; + bitmap=""; + + new GuiBitmapCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "48 48"; + bitmap="tools/materialEditor/gui/cubemapBtnBorder_n"; + }; + + new GuiBitmapCtrl(){ //horizontal bar + internalName = "RotationCrosshair"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 20"; + Extent = "7 7"; + MinExtent = "0 0"; + bitmap="tools/gui/images/crosshair_blue"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 70"; + Extent = "187 20"; + + new GuiTextCtrl(){ // Speed + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 0"; + Extent = "43 16"; + text = "Speed"; + }; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "RotationSpeedSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 3"; + Extent = "95 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateRotationSpeed(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateRotationSpeed(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Scrolling Speed"; + hovertime = "1000"; + range = "-10 10"; + ticks = "0"; + value = "0"; + }; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "RotationSpeedTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateRotationSpeed();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + }; + }; + new GuiContainer(){ // Scroll Animation Properties + profile="inspectorStyleRolloutInnerProfile"; + isContainer = "1"; + position = "-1 191"; + Extent = "185 94"; + HorizSizing = "width"; + + new GuiCheckboxCtrl() { + canSaveDynamicFields = "0"; + internalName = "ScrollAnimation"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiInspectorCheckBoxTitleProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 -1"; + Extent = "112 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Command = "MaterialEditorGui.updateAnimationFlags();"; + text = "Scroll Animation"; + maxLength = "1024"; + }; + + + new GuiControl(){ + class = "AggregateControl"; + position = "0 29"; + Extent = "135 20"; + + new GuiTextCtrl(){ // u + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 1"; + Extent = "12 16"; + text = "U"; + }; + + new GuiSliderCtrl() { // u + Profile = "ToolsGuiSliderProfile"; + internalName = "ScrollSliderU"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "25 2"; + Extent = "68 15"; + Command = "MaterialEditorGui.updateScrollOffset(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollOffset(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change U Scroll Direction"; + hovertime = "1000"; + range = "-1 1"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl(){ // u + internalName = "ScrollTextEditU"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 0"; + Extent = "34 18"; + text = "0"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollOffset();"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 50"; + Extent = "135 20"; + + new GuiTextCtrl(){ // v + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 1"; + Extent = "12 16"; + text = "V"; + }; + + new GuiSliderCtrl() { // v + Profile = "ToolsGuiSliderProfile"; + internalName = "ScrollSliderV"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "25 2"; + Extent = "68 15"; + Command = "MaterialEditorGui.updateScrollOffset(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollOffset(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change V Scroll Direction"; + hovertime = "1000"; + range = "-1 1"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl(){ // v + internalName = "ScrollTextEditV"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 0"; + Extent = "34 18"; + text = "0"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollOffset();"; + }; + }; + new GuiTextCtrl(){ // Direction Offset + HorizSizing = "right"; + VertSizing = "bottom"; + position = "98 16"; + Extent = "34 16"; + text = "Offset"; + }; + new GuiBitmapCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + position = "136 20"; + Extent = "48 48"; + isContainer = true; + bitmap=""; + + new GuiBitmapCtrl(){ + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "48 48"; + bitmap="tools/materialEditor/gui/cubemapBtnBorder_n"; + }; + new GuiBitmapCtrl(){ //vertical bar + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 20"; + Extent = "7 7"; + MinExtent = "7 7"; + bitmap="tools/gui/images/crosshair"; + }; + new GuiBitmapCtrl(){ //horizontal bar + internalName = "ScrollCrosshair"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 20"; + Extent = "7 7"; + MinExtent = "0 0"; + bitmap="tools/gui/images/crosshair_blue"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 70"; + Extent = "187 20"; + + new GuiTextCtrl(){ // Speed + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 0"; + Extent = "43 16"; + text = "Speed"; + }; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "ScrollSpeedSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 3"; + Extent = "95 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateScrollSpeed(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollSpeed(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Scrolling Speed"; + hovertime = "1000"; + range = "0 10"; + ticks = "0"; + value = "0"; + }; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "ScrollSpeedTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl);MaterialEditorGui.updateScrollSpeed();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + }; + }; + new GuiContainer(){ // Wave Animation Properties + profile="inspectorStyleRolloutInnerProfile"; + isContainer = "1"; + position = "-1 287"; + Extent = "185 85"; + HorizSizing = "width"; + + new GuiCheckboxCtrl() { + Profile = "ToolsGuiInspectorCheckBoxTitleProfile"; + internalName = "WaveAnimation"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 -1"; + Extent = "155 16"; + MinExtent = "8 2"; + text = " Wave Animation"; + Command = "MaterialEditorGui.updateAnimationFlags();"; + groupNum = "-1"; + }; + + new GuiCheckboxCtrl() { + Profile = "ToolsGuiCheckBoxProfile"; + internalName = "ScaleAnimation"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "139 24"; + Extent = "45 16"; + MinExtent = "8 2"; + text = "Scale"; + Command = "MaterialEditorGui.updateAnimationFlags();"; + groupNum = "-1"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 22"; + Extent = "59 16"; + text = " Wave Type"; + }; + new GuiContainer(){ // Wave Radio Button container + profile = "ToolsGuiDefaultProfile"; + internalName = "WaveButtonContainer"; + position = "72 25"; + Extent = "49 13"; + isContainer = "1"; + + new GuiBitmapButtonCtrl(){ + profile = "ToolsGuiDefaultProfile"; + buttonType = "RadioButton"; + position = "1 0"; + Extent = "13 13"; + bitmap = "tools/materialEditor/gui/wav-sine"; + command = "MaterialEditorGui.updateWaveType();"; + tooltip="Sine Wave"; + hovertime = "1000"; + groupNum = "0"; + waveType = "Sin"; + }; + new GuiBitmapButtonCtrl(){ + profile = "ToolsGuiDefaultProfile"; + buttonType = "RadioButton"; + position = "17 0"; + Extent = "13 13"; + bitmap = "tools/materialEditor/gui/wav-triangle"; + command = "MaterialEditorGui.updateWaveType();"; + tooltip="Triangle Wave"; + hovertime = "1000"; + groupNum = "0"; + waveType = "Triangle"; + }; + new GuiBitmapButtonCtrl(){ + profile = "ToolsGuiDefaultProfile"; + buttonType = "RadioButton"; + position = "33 0"; + Extent = "13 13"; + bitmap = "tools/materialEditor/gui/wav-square"; + command = "MaterialEditorGui.updateWaveType();"; + tooltip="Square Wave"; + hovertime = "1000"; + groupNum = "0"; + waveType = "Square"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 61"; + Extent = "187 20"; + + new GuiTextCtrl() { + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "16 1"; + Extent = "64 16"; + text = "Frequency"; + }; + + new GuiTextEditCtrl() { // frequence + canSaveDynamicFields = "0"; + internalName = "WaveTextEditFreq"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateWaveFreq();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + new GuiSliderCtrl() { // freqency + canSaveDynamicFields = "0"; + internalName = "WaveSliderFreq"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 3"; + Extent = "74 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateWaveFreq(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateWaveFreq(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Changes Wave Frequency"; + hovertime = "1000"; + range = "0 10"; + ticks = "0"; + value = "0"; + }; + + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 40"; + Extent = "187 20"; + + new GuiTextCtrl() { + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "21 1"; + Extent = "64 16"; + text = "Amplitude"; + }; + + new GuiTextEditCtrl() { // amplitude + Profile = "ToolsGuiTextEditProfile"; + internalName = "WaveTextEditAmp"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateWaveAmp();"; + hovertime = "1000"; + text = "0"; + }; + new GuiSliderCtrl() { // amplitude + canSaveDynamicFields = "0"; + internalName = "WaveSliderAmp"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 3"; + Extent = "74 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateWaveAmp(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateWaveAmp(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Changes Wave Amplitude"; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; + + }; + }; + new GuiContainer(){ // image Sequence Animation Properties + profile="inspectorStyleRolloutInnerProfile"; + isContainer = "1"; + position = "-1 373"; + Extent = "185 66"; + HorizSizing = "width"; + + new GuiCheckboxCtrl() { + Profile = "ToolsGuiInspectorCheckBoxTitleProfile"; + internalName = "SequenceAnimation"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "4 0"; + Extent = "130 16"; + MinExtent = "8 2"; + text = "Image Sequence"; + Command = "MaterialEditorGui.updateAnimationFlags();"; + groupNum = "-1"; + }; + + + new GuiControl() { + class = "AggregateControl"; + position = "0 21"; + Extent = "187 20"; + + new GuiTextCtrl() { + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "64 16"; + text = "Frames / Sec"; + }; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "SequenceTextEditFPS"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateSequenceFPS();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0"; + maxLength = "1024"; + }; + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "SequenceSliderFPS"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 3"; + Extent = "74 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateSequenceFPS(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateSequenceFPS(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "How many frames to display per second."; + hovertime = "1000"; + range = "0 30"; + ticks = "0"; + value = "0"; + }; + }; + + new GuiControl() { + class = "AggregateControl"; + position = "0 42"; + Extent = "187 20"; + + new GuiTextCtrl() { + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "33 1"; + Extent = "43 16"; + text = "Frames"; + }; + + new GuiTextEditCtrl() { // size + Profile = "ToolsGuiTextEditProfile"; + internalName = "SequenceTextEditSSS"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "150 1"; + Extent = "34 18"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateSequenceSSS();"; + hovertime = "1000"; + text = "0"; + }; + new GuiSliderCtrl() { //size + canSaveDynamicFields = "0"; + internalName = "SequenceSliderSSS"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 3"; + Extent = "74 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateSequenceSSS(true, true);"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateSequenceSSS(true, false);"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "How many frames in the sequence."; + hovertime = "1000"; + range = "0 100"; + ticks = "0"; + value = "0"; + }; + }; + }; + }; + }; + new GuiRolloutCtrl(materialAdvancedPropertiesRollout) { // Advanced Properties Group + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 0"; + Caption = "Advanced (all layers)"; + Expanded = false; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "202 16"; + + new GuiContainer(){ // Transparentcy Properties + Profile = "ToolsGuiDefaultProfile"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "210 89"; + + new GuiPopUpMenuCtrl() { + internalName = "blendingTypePopUp"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "3 2"; + Extent = "83 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"translucentBlendOp\",$ThisControl.getValue());"; + ToolTip = "Determines the type of blending to be applied on the transparent object."; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "LerpAlpha"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "alphaTestCheckBox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 39"; + Extent = "106 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"alphaTest\",$ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "When enabled, caused pixels under a specific alpha threshold to get discarded rather than be computed. Only valid for transparent objects."; + hovertime = "1000"; + text = "Alpha Threshold"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + + new GuiControl() { + class = "AggregateControl"; + HorizSizing = "width"; + position = "100 39"; + Extent = "187 20"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "alphaRefSlider"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 3"; + Extent = "45 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"alphaRef\",$ThisControl.getValue(), true, true );"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"alphaRef\",$ThisControl.getValue(), true, false );"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Sets the minimum transparency value that a pixel must have to be calculated. Anything below this value will simply not be rendered at all."; + hovertime = "1000"; + range = "0 255"; + ticks = "0"; + value = "0"; + }; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "alphaRefTextEdit"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "49 0"; + Extent = "27 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); MaterialEditorGui.updateActiveMaterial(\"alphaRef\",$ThisControl.getValue());"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "100"; + maxLength = "1024"; + }; + }; + + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "transZWriteCheckBox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 23"; + Extent = "112 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"translucentZWrite\",$ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Can be used to help force a proper Z-Ordering when Z-Ordering issues occur. Only valid for materials with Transparency."; + hovertime = "1000"; + text = "Transparent Z-Write"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "translucentCheckbox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "89 3"; + Extent = "107 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"translucent\",$ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Sets material to use transparent blending modes."; + hovertime = "1000"; + text = "Transparency"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "castShadows"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 55"; + Extent = "112 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"castShadows\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Object casts shadows."; + hovertime = "1000"; + text = "Cast Shadows"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "castDynamicShadows"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 70"; + Extent = "112 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"castDynamicShadows\", $ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Object casts dynamic shadows."; + hovertime = "1000"; + text = "Dynamic Shadows"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiCheckBoxCtrl() { + canSaveDynamicFields = "0"; + internalName = "doubleSidedCheckBox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "105 55"; + Extent = "85 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"doubleSided\",$ThisControl.getValue());"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Determines if this material will be rendered from both sides of the polygon, or just the \'front facing\' side. "; + hovertime = "1000"; + text = "Double Sided"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiContainer(){ // Reflection Properties + Profile = "ToolsGuiDefaultProfile"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 95"; + Extent = "212 25"; + + new GuiBitmapCtrl(){ + position="2 2"; + extent ="192 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + // Reflection Properties Text + new GuiTextCtrl(matEd_reflectionPropertiesText) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "91 6"; + Extent = "80 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Reflection"; + maxLength = "1024"; + }; + + new GuiPopUpMenuCtrl() { + canSaveDynamicFields = "0"; + internalName = "reflectionTypePopUp"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "3 6"; + Extent = "84 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.updateReflectionType($ThisControl.getText());"; + ToolTip = "Determines the type of blending to be applied on the transparent object."; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiButtonCtrl(matEd_cubemapEditBtn){ + internalName = "matEd_cubemapEditBtn"; + profile ="ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "143 6 28"; + Extent = "33 18"; + Command = "MaterialEditorGui.showCubemapEditor();"; + text = "Edit"; + }; + }; + new GuiContainer(){ // Behavior Properties + Profile = "ToolsGuiDefaultProfile"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 122"; + Extent = "212 80"; + + new GuiBitmapCtrl(){ + position="2 2"; + extent ="192 2"; + HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + new GuiTextCtrl() { + text = "Effect Colors[0:1]"; + position = "1 6"; + extent = "86 15"; + profile = "ToolsGuiDefaultProfile"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "89 6"; + extent = "16 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiInspectorSwatchButtonProfile"; + visible = "1"; + active = "1"; + command = "getColorF(materialEd_PreviewMaterial.effectColor[0], \"MaterialEditorGui.updateEffectColor0\");"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "effectColor0Swatch"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "109 6"; + extent = "16 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiInspectorSwatchButtonProfile"; + visible = "1"; + active = "1"; + command = "getColorF(materialEd_PreviewMaterial.effectColor[1], \"MaterialEditorGui.updateEffectColor1\");"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "effectColor1Swatch"; + }; + new GuiCheckBoxCtrl() { + text = "Show Footprints"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "1 24"; + extent = "93 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "1"; + active = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"showFootprints\", $ThisControl.getValue());"; + tooltipProfile = "ToolsGuiDefaultProfile"; + tooltip = "Enables Player footprints on surfaces that use this Material."; + hovertime = "1000"; + isContainer = "0"; + internalName = "showFootprintsCheckbox"; + }; + new GuiCheckBoxCtrl() { + text = "Show Dust"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "110 24"; + extent = "68 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "1"; + active = "1"; + Command = "MaterialEditorGui.updateActiveMaterial(\"showDust\", $ThisControl.getValue());"; + tooltipProfile = "ToolsGuiDefaultProfile"; + tooltip = "Enables dust particles on surfaces that use this Material."; + hovertime = "1000"; + isContainer = "0"; + internalName = "showDustCheckbox"; + }; + new GuiTextCtrl() { + text = "Footstep sound"; + position = "1 43"; + extent = "77 15"; + profile = "ToolsGuiDefaultProfile"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "None"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 42"; + extent = "105 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "MaterialEditorGui.updateBehaviorSound(\"Footstep\", $ThisControl.getText());"; + tooltipProfile = "ToolsGuiToolTipProfile"; + tooltip = "Determines the footstep sound to use when the Player walks on this Material."; + hovertime = "1000"; + isContainer = "0"; + internalName = "footstepSoundPopUp"; + }; + new GuiTextCtrl() { + text = "Impact sound"; + position = "1 63"; + extent = "64 15"; + profile = "ToolsGuiDefaultProfile"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + text = "None"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 62"; + extent = "105 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + command = "MaterialEditorGui.updateBehaviorSound(\"Impact\", $ThisControl.getText());"; + tooltipProfile = "ToolsGuiToolTipProfile"; + tooltip = "Determines the impact sound to use when an object collides with this Material."; + hovertime = "1000"; + isContainer = "0"; + internalName = "impactSoundPopUp"; + }; + }; + }; + }; + }; + + }; + new GuiBitmapButtonCtrl(MatEd_phoBreadcrumb) { //Go back to previous editor + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "-1 0"; + Extent = "20 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + //Command = "materialSelector.showDialog(\"MaterialEditorGui.switchMaterial\");"; + hovertime = "1000"; + bitmap = "tools/gui/images/folderUp"; + tooltip = "Go back to previous editor"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(MatEd_editMaterial) { //Select and Edit an Existing Material + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "86 1"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "materialSelector.showDialog(\"MaterialEditorGui.switchMaterial\");"; + hovertime = "1000"; + bitmap = "tools/gui/images/open-file"; + tooltip = "Open Existing Material"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + // New Button + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "106 1"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.createNewMaterial();"; + hovertime = "1000"; + groupNum = "-1"; + text =""; + tooltip = "Create New Material"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + }; + // Save Button + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "123 1"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.save();"; + hovertime = "1000"; + groupNum = "-1"; + text =""; + tooltip = "Save Material (ALT S)"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/save-icon"; + }; + new GuiBitmapCtrl(){ + position = "147 1"; + Extent = "2 16"; + minExtent = "2 16"; + HorizSizing = "left"; + VertSizing = "bottom"; + bitmap = "tools/gui/images/separator-h"; + }; + // Revert Material + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "151 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.refreshMaterial();"; + hovertime = "1000"; + tooltip = "Revert Material to Saved"; + text = ""; + bitmap = "tools/gui/images/reset-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + // Clear Material + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "168 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.clearMaterial();"; + hovertime = "1000"; + tooltip = "Clear All Material Properties"; + text = ""; + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + // Delete Material + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "185 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + tooltip = "Delete Material from File"; + text = ""; + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + Command = "MaterialEditorGui.deleteMaterial();"; + }; + }; + }; +}; + +// Here are all of the other gui elements that were included in the original gui============================================ +// EDIT: Instead of showing the faded bitmap, were going to just go ahead and push the controls; that way they are sitting +// on top of the editor gui, while being nonmodal +new GuiControl(matEdNonModalGroup, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiOverlayProfile"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(matEdSaveDialog) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "197 221"; + Extent = "336 104"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Material Not Saved!"; + + new GuiButtonCtrl(matEd_notSavedWindow_Save) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 69"; + Extent = "121 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "matEdSaveDialog.dialogSave();"; + hovertime = "1000"; + text = "Save"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl(matEd_materialNotSavedText) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 35"; + Extent = "318 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "This material has unsaved changes. Do you wish to save?"; + maxLength = "1024"; + }; + new GuiButtonCtrl(matEd_notSavedWindow_DontSave) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "157 69"; + Extent = "80 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "matEdSaveDialog.dialogDontSave();"; + hovertime = "1000"; + text = "Don\'t Save"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(matEd_notSavedWindow_Cancel) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "245 69"; + Extent = "80 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "matEdSaveDialog.dialogCancel();"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiWindowCtrl(matEd_changeCategoryDialog) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "288 144"; + Extent = "248 133"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Change Material Category"; + + new GuiPopUpMenuCtrl(matEd_changeCategory_categoryList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 32"; + Extent = "183 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiButtonCtrl(matEd_changeCategory_okayBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 97"; + Extent = "137 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.okayChangeCategoryDialog();"; + hovertime = "1000"; + text = "Update Category"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(matEd_changeCategory_cancelBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "159 97"; + Extent = "80 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.cancelChangeCategoryDialog();"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(matEd_changeCategory_addCatBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "200 60"; + Extent = "39 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.addCategory();"; + hovertime = "1000"; + text = "New"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextEditCtrl(matEd_changeCategory_catNameEntry) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 60"; + Extent = "183 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + new GuiWindowCtrl(matEd_changeCategory_ErrorDialog) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 18"; + Extent = "232 113"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Category Change Error"; + + new GuiButtonCtrl(matEd_changeCategory_Error_Button) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 81"; + Extent = "80 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MaterialEditorGui.okChangeCategoryErrorDialog();"; + hovertime = "1000"; + text = "Ok"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl(matEd_changeCategory_error_Text) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 31"; + Extent = "215 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Text goes here!"; + maxLength = "1024"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cubePreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cubePreview.max new file mode 100644 index 0000000000000000000000000000000000000000..6feb85fc43eeabfd542c9258061754e1b6fcf5b6 GIT binary patch literal 253952 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 EKgRP0umAu6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_d.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_d.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e61b7543a34aa4f843a3ff407451357618d7e55f GIT binary patch literal 830 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2*V7J z!+?%N08S1rMg}Hk6j7jWnORxb1Q-~Y*f<%0I-$ye%2-5LMcEhx6@`Sw*u^=N-6aeU zDj6pitC+N?nodnqH*Y^KDF_TLW;RAPewZbKAoGM34IPD8L?#9vau!u}O>EqF(I_aX zY0{!i%_=Rd|8FtyFaw<-$SlZU&oIgI>dHH<lke_Y@cHuIlB+LkOYXU!D>}4j@$#VM z?v;KoOZX#yCQNspu+;sct5xV!fmh)=lTJ7=1ghR)5|EncAFl4X{HFJ;HA#B|cc^J3 zi>18Wdgn@Ulh&g>8^U*4YMyHi@7i>7PT!MC1%)dT6Ze(Y2VILj%#rKG7al#M&FNy& zyU?KQ1-D;bx|Q=I_$<?w`;m9TOfGlYzLArAndK8*Utla&HEZ#Q-W^99-IO*;80RlB zdX$j-WqST?Z!@dBZ3`!Ec_APmbD+bzbGOyZ>81{T3<tlTVLfVIeJC?J-h*kGzwUFN zLJ1ZLhKBT4Gt1jp7I=6BC@$38bjn&V{O6>J`n<ow1ixk7sSH2Odiwpt1D|8RT-bfF z>vm0M^-BKx_2)F}YyWMxQ!oE}FUPlL`kREaD|fHG<v718?bw;U+wM<Dp6r|#l(kLz z^_#Q*gjFN<sv4)3_ML6&6=-;79$DJr9&&bVxzx;46IefrYuC>_d#+-B&NH#AJ`wv) z9V#e1T&K(ZeKKpD>HMWTV{d0#I~{hY{;+b(q2;G}uHTYf_<FK_zWRlmvyD^or#zkE lztyzT$~^PyeCM5u-=?0P-D#)D{?Tl%s@6OvcLwGEHv!2V5ZwR( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_h.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_h.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0dfc3466b3935dc2f44e363744ca2ec42f68aafb GIT binary patch literal 869 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2*X^E z!+?%N01i$zMh2ky2oVMbW)=n}MpiZf1_mZ}HYQdscBmvH6Eh2|C>y&tgOIR@n1mvS zppvqxn&Cm?<YE=m)@hRI9w#_8Jv#+~!NtPF%qa*nQ;<=Jfk{|Ik=f9Q#j$Z>Qj-g- zsA^#1!i^V$CT%)o?A&}<#iYgc|1Aa{W}sUHnFSf_8G4GnoV`-*Z$F=AF;8^TrraQ_ zo&-+`L6tC}ZEPkXw`#mt-flZ}DWm0$u&LkmtgTlAm-}r=Su|JkeP@qSqTrj$X*VY? z{*=Ca+Rr5uRtKz{vQEi_d+yv~oAonSn%oe5cjQ@u=ci4#xpWz)ZJ5dEYZ?5bi!))% zjh7B{T)KU}%{EN*tYqBN<ScN&EbhKiOy<cK%O`P$n--MZ(H4E57qhqaY>6nx<S6l^ zsQs>&ekgmsI(xu!tJ|EyonM9L-!<N0IcIxn-Se=sQE&F<d(DfrPLgWaBfuyjA)E7B zeP8-jmX*90k~6mKSa$wN{3O<`9~DC`9Q?EI@k~WVMK0$PX_KsvD>OEEu~~3@c$j^r zYp?i(&ZU#8CWai&Ib&CH&w_8;KN<hj=hAGs5tCkassG*ZpJAT;N^R4>>#zQr7?=2Y z?+i{8^OKu7xqja))m(1WHY@G*UC-0OOKX+Bo~Vcqd6^UQ_1^nqM-RWcvx)Osm}Pvw z>!j_mw|2-LGI{6BomptVw|dH}UW@Mso0Ixp&hv3!cSH8)SDW&uaYYZ(xNE;WdtYAe zzU<Vu8@1*YH*6OKUSZciq^y~_GvHHV?w^*whw9a)o==<Kdu86_Ywy=+scra~z5V)) iyDP8Fx%cdT-?ja%$17ZZTibVfEf<;gfpK5*|C<12FDZ`z literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_n.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderButt_n.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f64864a13d31f4db35dd3ece89fd69828bb45cfb GIT binary patch literal 654 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2tysn zVL(SB00$=*#4dyo0}CrN8@m7l0}C@73o9#3f{_U*F3QHBC?qVxE+{6>VR%qUS=G4M zR82yVfsvVsg@qla6c{*yOhU|xhQch4fsG3%9#k??R$={ri-Cuk5$G6ZK?ZvUgYR!c z-|?z$>OEz4Yf;g%_}J`i(@OnxUOnEvOLmzyYrtNo5Is(RUN4P{q1z9bS)^%hnBc(n zyUOfcQR{-Fh?(X|JEIl6B0dG~Z7#Q7mtEoXz<TZO6qdr%^53*Bttn3LV%g)da_`xb zPO2ApAE+4eFlXMnzPLB&qo#?&gM*oiu3ZR}-jmvK`$g~must3j1?92xZ7y;Mas<is zuTy+v$-=`FV0ggo^&0sLEH@a^!zX&&PAr!5^0xhFF+clEu3g;KEyYnvTc3!OzbjvQ z*Hk@By~r-XQRC^JmG2E-#F|XH8-D!x>8&-TcIS9vY9n%L&rO?u>dStfyO~+5_DXJI zUii;trjc*eef{f;&(AB0nXViVQ@qmr^ywY7x%zvjALsohezh<(_wsJtw)JeK3A&-8 I_5W`I0J+q%@&Et; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderPreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_cylinderPreview.max new file mode 100644 index 0000000000000000000000000000000000000000..7259a5df0f517aa8221351b04ab0270ed4a14750 GIT binary patch literal 258048 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_mappedMat.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_mappedMat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4328f2a4d22b39c2c349772a0a5fc253a366d9f6 GIT binary patch literal 409 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!QjB)z|1Jfz$D1XEXer(2!kKc zVL&$`11_*t$RZ5voE!q^$`}MOME>7m;9+J28qO@pV9zl5>f7@R7?@7o3TSO$;JA{u g>jeXY&f29M5ey8yZrSS^uuB2$#UqPN%KraN04ldb?f?J) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_pyramidPreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_pyramidPreview.max new file mode 100644 index 0000000000000000000000000000000000000000..06b4b48d110ffd377fb8945686059ca0420d08ea GIT binary patch literal 253952 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_d.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_d.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b693e34d6b416b52ac09982837405ae8b4c98184 GIT binary patch literal 891 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2*X66 z!+>r?0vwz`5oQ!oCPro!RyF|!24)scCKeWUsA@(gW<eHKF$N)F5m7dFaSla8C1nXo zN2g?!W>vFk>h9@+42;aoj7%K-Fe?}ZnS>aag+&Y#9RnLDF5H;p{NdomhaZ)kikcP~ zC$p%Cs{X&lz{AW4bRV-IgFS<Ek^F>Tw_dK@^?J&udAnZ4`JRq65BkcWyz#;no_pyP z(mOZT6l$nNoepqW+@bXD-McPUN0kYhTuzhDnn?)z{A=ym*mOheMqGr*f=AM;r#+87 z^**Alsls2DH&N!`QxVOt>KmW1Z8{RH)leF<sA~4y`LgpkKdU6ImKI*Z#`r?B(}jJj zz}6JeX8uDqQ+Q9e-zv9UHSyIp-M8QONw=<?@$5fCf@u1i+lG&YL~nT?|0v@Xcckv$ zp-YF>&kno1R>{6|%@k=KZV^t+<!!IB>zO=Gt6jBL+N%4rnWMI~sCxp_w@EF2%4>Gb z7A_8X_#pP?XRBjZH7A<~2zWdbIbz)~b;@jqZk;$W^A!O*!XEE^eBDj_`uB1cpH;6! zSQxU@F05NP^TJa7!i+!Ftda_>rVPR%#yOwcw=7dFsNPd`<gRpYyj}FInN^<?ZhaHo z_qHc@dT-CN9RDlpZ%$Xad_LTFM{u)J&6Yb)!?%CaUQvB}|Bq+S(%)bHBxY#nyHTdf zXLnt)ep1;q@&63G&AK(mE?s+V(i0=l&~?sZ_Fj?L<>%h7RMp;SmEpRq?ox4gpYm(I zuQj^oOUxEU?<w7|*L$Bf*PGSVpJtu<s~Ni1=g`BrE0-4^_xzE3ugGof_YZzY{xf8} z{r2KN!@T(X-Ic%d+@}3!5WD{3a{Mp*i|>pdymb<K9{R*<_1BWU&unH-4W22q|Nl(@ D->Nju literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_h.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_h.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3a8ca6ed33c8d349988d109197d5097ef92d035 GIT binary patch literal 929 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2*X6M z!+@@Y19lE3PDTcx1#l4%gN2Eik(Et=fq{vgor#5=5h}^Z%*4XVCdMw#ASfg(A}XP% zY^bE7>X_U*EuBNdLy!R|&&0~c4>O8UkV%L^m|4Wov2o%-#lS=(XO+T*8!tW#N@`l9 zoXp}Ps`~#H0}nIMdO>DE2788wx3|4LalFsXvy7X~`vjYoPb8z-ZDYU8Z7Fy5<k`Gc zpSx3E_FLCUSDh`fpQU)kHoGoJO4_tZbIFFPLvH0P7hQ#|`rb}Elx(o!dG4yDt7;1K zg3M=K{&Rc1>bssUR*kNz499Ebs*l%PQ(Y0WaI){F3)Y5-Gd`V4XWVrzS|^|7giqV# zJsY%6s4!pf6uRV+Cm`+Eb0{&oBK^>{c_QME1SXYze!j$_|7-Bn!0mD=R}>G{)~YSb z%h8V5Dai9qTI}GK_tyTtht)&3m3=PwvgPTw%G=X6Je>CGh|02Af}*CM17`fJID2XL zQm-Xp{j$d+*Sz*V=@jiVd4leCwWZdl&O{im%+7iCW`FrC7Zs-jB~FEP+DsnpU7BJF ziW!EjGa|Cr^DeJ;<yUxQX`B4ibm`TvrA$F4p9L8iWLWP>obh_sv8cb}p1nN7Hctl4 z1xy*6rrJ$iUUoq*{iEI5485-cdn;Q_t*SphN&9uiJn7WuyQR}Ncg~!CVcN;%%ht}$ zS+=_K!&RM+MccdPuMb?#mdItQ^U`hmMAI&j<_KxA$2`CNK5lvPPP{s8wYmQ3(phEY zs;xF#cjxxUc{G|7X=PpwEw6s>y=h|HDy?}{vs6PfdZ(1fsl767T(mm$nfR5`%dZyt zy>qkP@Y*_J-SJDO9z4_IjQGB6o}Ov(TlXzD<xDgGGgK5G{^K0-!};f=_JCI>zl$Ub q)+%TG+*bW7e$$`ooAp@&)4s-bdVlv>o}KsJWy$P`W^3#J-vj{RtUgWv literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_n.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_sphereButt_n.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e2244ab55607b9b65715cd498cccdb93809e8abd GIT binary patch literal 815 zcmex=<NpH&0WUXCHwH!~28I+MWcdGvLC~c%IlGd9k%5H)B*^gp9Rr_ZN`6u*L&^c5 z2;=|T3@r?d!~-S-V1@&zb|ywfpezu>C5UDGKfoZ!!63pQ!ptbhz$D1XEXer(2tymt zVL&$`0S->ERY)QXEUe6I0t^hyY|I>tOzcn@MkZ!KAr=u<HepdQ21P>^c5w~~B}ZkW zq~??<s;+_z3@ps7tQ^d6a~K7g4275-8--a!6ay0vKKxKPaiNk^l4z5%%KuvoJj{$h zM==XB*fYpfGCpCwkiF>fhBexKa;FwW2E5??T+_wUwL&?dOJOCy`{KhZR02XpIa`ud z4$pXyH0Asb=kJwg9^d$*70t6KdCo$|bvf@DS4W?mw>|1=N>=ArwnuB1xo%X_oUZGo zwcD@TCyL$SMaYy6i6ja3f<>A$79C`~k#qK$M7H7lx{E6>ElcTeno*sY^Xuh(OSyb; z|Ch_JR!94IKKXIZR_D9Xcgfhd)mgiaWtNH^w0QS6L67m#!<#33{xhiMW^G-2?cIXq z!LCW_(K}X!>l!KgzC3z)^+v6b2}bOU0)~pm6aobv1*<72Zdr4icV@fpvVDpnJ$*CP z9#r-Aw>P+)TDQ=_f%V8O{uOC?n%+mE?Bf_W_MGVS$#F>&uU)m*mi_D!Wy9Zl@2+Gk zmWo%qG}-8FJ+t_n(*DGn8)s)W2ft4HdA7u=Cp~Ya&Q#{lr{2eYIU-(D`0QU*vEP~3 zVUH$@Yfk#<ZTn-{&akcfbyudFT&j1;@wmh7ELtC9ae!f0=nJ!Ljc2QO8(Ij(-a1*b z=h)B6wVK}x<dZkg&wOUTJ9A6jc7bK{KebOcSpI71x4Ty&-@n|H|JmeU<nNf-YE%D- ZMXLUah@YOlZ)VO^JxBYjkMjR-0s!@13GV;^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_spherePreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_spherePreview.max new file mode 100644 index 0000000000000000000000000000000000000000..71485ac8621811e3436d8ca73b259f0b33b83627 GIT binary patch literal 249856 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusKnotPreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusKnotPreview.max new file mode 100644 index 0000000000000000000000000000000000000000..4e2bfec1025414e6013c75d2a043a2dd9c63a337 GIT binary patch literal 217088 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusPreview.max b/Templates/BaseGame/game/tools/materialEditor/gui/matEd_torusPreview.max new file mode 100644 index 0000000000000000000000000000000000000000..4e004500d307ef1ad10067f472e3f656167d0655 GIT binary patch literal 217088 zcmeHw3xE~Hm3G~EARx+sXriL_pdcWUVF1Mk&Yj_*_(Ua&N^}MoV03_y8GI&sC2JmT zl11}QbVJ_Jtci(vY@T9FvN4O%m`yg>bvNoJyV*w)lXW-l{%-ug?^IWHSNH9{Gt3Nd zXR6`!tv+??RMn|d)m2|t_dNCFq-Q_;xuc#_iQ_C)r~dfL1T`k)7w~IHSPe>@81nx3 z$}6vgVhTZjRFMTtyZK*l0dE0s0dE0s0dE0s0dE0s0dE0s0dIkkZGr#8T9xbfT2!+R zR1e~s{~*v<&^XX|(7~VyphG~1f+m6h0jNo!$)LkPQ$UA<jsU$1^lH$Nprb%XgQkLx z0UZlE4itbILDN9TgH8ZV2h9MnOr%)|&j+0p@=nI@T+nMkO`v(8W{{Cb5`TCLcnf$7 zcnf$7cnf$7cnf$7cnf$7cnf$7cnc&faEaQAe|=bsFH>9ayG`AU)pY-lBY+qfl{H9q z>)ZZe>h3#sE&I@~>!!?Gx`#`Bd-QbO|E+~HA1}-VXEzuAI4)J|R3~!o)RzN3Pf#^B zf0m;^3Ruo>wDPRfQq_%rOH~(Y-ifmG!5Rax!BMKFK+J^rNq_TazK0TA!LbCncc@<c zOP7JTN2Mb`{S98z-~4I%*#vTM#Pjb!?K?EP-{&cNVgJjZ{pZ^J$bTzZe*^5lvEO;d z1rLS&3t$kt>TmsLL-sQSrEpw^oY$k}x2lz(URdB_wH?=_{v5|5f97kB{-jw8p#`B} zJ$k@)wHg09#P3#!HRyxn^r3XLPxdi0%%ASWGGt3eF>?_7*tPn)9)7nKe$O(nANCEf zJd5_C{nsJuXF~svO11YPWBdNrfA4cDpVH?aS)jayhL;=V-ar2z>eBf9BMS_*ZTq7y z%H6krf9y5XGT#1?1%}$T{m~cY?)|?%_8Mv#Z~w>wLv7ps=!<gq{@)*a#bqW1yb4#t zC5DxHO8=Dj%wIo^=kJ-b#$0*qgtn%3KJv$d{g-Cf$r}OO4WOUnOF&j)O6`z~BG0Zs zK@#N%)Kt<?Cz}$KD#^PEMYcY7Cg}YJ{N;L(WX{VZtV#<42Dm%H987y&z$@-&;4xQr zHOQ6*%`oRfxynbT3j_Xe%L5N?k4PwRt#-v1?rJQ(^_l6B)uszp;2K4*Ukjntc2lz! z;C2Zf1u0-LxPyBxRMCyB$&0Mbi>%X;s;t`jyvQ+mkq71FIW{llxN?z=tTT@Fu4k}` zT{Cu864+2lU}GhLO_c<?DhXU$N#MFl0^OAazE?@$@k#>UuO#r-t^k|IZHqmX1fHlQ z@B>#M-P}*Q0_ie5RY~9(S0J6mKU5O<$4UY}uO#qnC4uKE2|Qm(;GZf9{30!o9#Uyv z1-OR?w_o81hg35fGA=P15*JnKayc7uGXm4xj87zKk}=Nc<R-GB!(lFj`;_{coRed6 znNk#{Q_`qJxm#qTS^Qp5!mZ%;nkH8c<r9u6F=9)kX3?h|9{bars|C(gy`%9>sIs<5 zGc$rFx3UCf-O5s^o+s6#6P9STs4h&Q<Hbg}8WT2A5n&u9oh+)mF`g;CzE#HKbrTmQ zc``k7*T&zw^iktwr7yMQ<<HvW=bxXRY4g(?7cW8*N5X!z^<dI-&N7(v+?Sc3KEM8i zT?dn%6Q04OAD>Du@V)Ju^sl`3=j<v!|NQhE)+(i!d8PV4Na851Y~;^co^a*%7M8Yw zC)LIi;sd#wrmxHdR0Vo8m7JSIBHrKhywq^m!if>FW+(L-0d#?twGM}|;~WmFSnqHs zGbY73*x^XYD5&0~ZB$}&>kYg74KaI6{U_Ot!11X+WeVyZ{;1tN=|0}bAP%iQM?g1x zOrX@_YS3d@j({$9jzGP%LC*4HOeKMX!g}ax7FjX3kYg(ejH@ItzLLPft^n=kmSKWh z3tC<am<O_l=yeVpnn$sqEqjk{qEbP8ZI#3!7XK9QnL;5GKXiv;;d>E7y>dIALfx*F zkX>rg4Z?7f=+wPM;c4^fYK=Nuo_%)9)4xr4_7vdhQy-p1t(WIsy^vUmCt+LFwRkqg zXS3w2LaJq|6|&7(tu^8I0(nx_3CS*my1`i{8uYW=q>kjZ*4c^YUp?|Hwhwu&!PCeN zc_Ow3x$+5lx8$v#$wfK2`D{eVyO35(G42`(_ds(eu5>x)qBcFyUle-;G!2e(QPX9p zS)i6fM>pi%20c`F|JpmG4;FcL1tz)zT-j=YkBWZo)@Ns~F)h9W7t2mxb9P5>ch|;E zeZi%D+t+t(?On5S+tzD4*Y)+TS#|cZ)}VQAQ*c2?U+1>2j_zRD`mVmM+l++yg=FXE zo^A-O+1RnUb4}mYt=+wAHiwDAG#j^fk+p37HJdwndOFub=iF<1Hp(jn<reOGN~4EL zRVqjo#9j!|rQ9h`Evm(%DCguYn@sJ7+90?Tg+?FUG%r1c^V8?wa;Q5$eSZGznw8S? zrlHgdIh$#A_=H<t-=I72NZwc^L1YM>Ua7E8ml^(H3_*T4?{pndzaOH);lD^#u7GaE zm_TxLuW?7Uw1(Qy4m%j7TRUr+BHcuNM+A*Q%-F{`_65!*m_A{YhZ@0C0Z$4y^alPI z6KTkaoNIYPe4)r6V{3Nt9;7`UFQ(K=j7dos4O|Y=H3`QQ42;-OYSDM!PitO?JWUHF zn(zyi(i7+*Y>-4nYFC11i%#LO7sojtc>nv)n&5C4dq|3NXo@p2#bHXNUMR><!f(b4 zlYTQn?mF5nW@#?YESbf=-us>-S`4{@h5icGxPlutY-lm$dSt?%Sa?^U&ULu7z)(yl z=u@#7iz$P#&seuM5VnU~*m1HEsEnq@Fu{0NV9%aCi(L&Gy0jXefIzx`=_b}~+MzWk zU;tvN30K5@9f#=)Z16ywGawtiXc}GE#xtys@jADJLxr<xIMf(<$;_>VnP~116-8=r z1qyDfQp^>o2tg+tC#@mjIB9|Ua57zDl*$FMMMC{F(-Me~u0^E8t%tG1J))w>vnx<= zJpv68PPmQpCAtz6U>2y!)udr&H7i7td9QH=^uvPK=$|Z0eX=Y`0lh4<-l^2?#Ogrb z1Y-iGHjS(~j(*I{^^G=k9j0+>q1`@~MN%Nqh0wIcbQhvtb9^?lUC~R>t_N54aP}Ry zm|ADNm<dy954VFZ3*&0gcCgLGp}-YLbOGk*3M3X%X@SJpnHD%G-ACLk^iAFtGF=OO z1I-Z_?+yhlLw<9;qGz6E1|+lsnyW6>T<>Rr<fxO9YOfN3BH1!D>5yFRgOj$ODHxtA zv0#Nen6sXS%Scod9Fl!lU+#mDTQKZt6^rSC%UG<`Qsf9#;lx=@S_IZsIGj^ORnd6Z z$iz6xc)fOrC*=mF-(lt$(6M=KSLc?#3p#pmZ|5<|VYY7Gx_wKZLKja(E!)zuw!2dz z^b&jR_RT$74{bt2g&eCS!M7U7XJOWNZP>8Aw^J)&aafp$Y4xO|tSUxvCcM6@7i+zB zottgtm;v$eM2uCM5pOj!p2O;fu5PR|bs0I5GdIh#+EOuA4`XPYjon*&d%L!D_V#LB z9Le}*y2H2n%EnrqjAi+@_H?Z4>bqGh<b8++jF?tiI?AeI6pOd5b3-@Q*Iiq;Xl)## zSUiVs6_$;)IvE@4+}07(NppreBfiyHHrDE7EW3YiXZMB`-QC+a>tfQ&`Hp{_Z8esQ zv<ewXbM^LguG`+-vF-fM8#=qSQuY~sC?FYS6=w-p<rLt6Z8T`T>{gs>#<*5xnK-MB zarwjlCn15O%<vCa!SG)j8~z)$Y<P~{jFZz6_N}r@0HZ+<6EO|h#iX^B`mUUlzzL0| z<Jq@(=;@vQf&wlR&pJdz3TGzn;Dk^~vqUCSP`ECcA{^z)r(o6G6im)Or+}u(kyu@D ztT``WOtfc13}gz)i$_kQzyh97Ej<L!&{qP(-J~>pxDdaD9J%>kxBRQ5_y#fv3$1tu zWKW~v=*-NE-GRM~pS++KCkmAZBXC?%MV@Zp2}LXv`D2RM1o$9OCxey(3EY6<Z3iaU zr)ovYRCFwA8aN#4XcA5qq~I>Z*wOI{1df0wlp|{)_EZ?JTgH^L+@2spAv9I|!GP3v zzwZf>LTO(B)+$G0PtcKIvZ)hIDb4PzPT7_C@{$}02c-cL3f!l>v|drVplQhhIC`%7 zLD86eq}`NO>4G~<wg15#I-PF%SuBto?2UGvVSpX}x`H0uft_}X#ln-@hYlEUhda^| z-tCSlZ1AB1ci2;0JJ6YU?2vPj7p}kv1$VM7agVkH&q+&+0WUUdF!K)6IX$@J8f?5+ zGG?%(MfJ@uCNOw#C%auk59gi6EvD`Ou~H}x?u4@`*HFU%?nKRHFceH*1**`^WuS_% zJVkocK0E2!n}TJ6JDlL`Gh9b9atr3c9eM>0`i%#7I(qEd+=DwEJr<1c;0|wn2Nt*! z4gZbo!1jpUjLjs=2&C`UiQ9RNXo+bU&EU=nFh+89AqFxlWnK&ixWjLga`}23C;+kf zrh$$JO$Q|kl?U$dQ>UuPQx>?>j4UdFI|?-oM2o39H8u%n|A9L*Q3iLCW%efRVDWUd zU4aAUg|8D)zCnXKG(f@^TyTe&<A?-z=AdG_WzG80@7#e&oPpc5PxbBGIVtVk!w2rL zr?^AKXa#q&E#bi(I0=sG+_{t8uHoI%gFDi?MV{TEfU6%KnH^vlz@4bM42I&toyuU5 zZZ~5w5AJBc_25ppo5zDYvHd<C+_4ro@W7pD_~+t3I$|??5AH;p^lM<Df$iLBMvBDR znJ9;=ayY=9*~o*-*ONhWLHv3Fw^Ymn%?Bk4l?U#~+P8=_nc&X+ss(pWL9N_bSoMHA z3sLq#gF7@p!WUd{rwsxl65Ki6HoXUTU<&^ItHkCB(>08Jst?>b4b@6`_wa!`>?z5y zq(H-tx}7`OmdIS&`q#2t*BPfOY#<#ko)w#@C7o{=cJ5@i>kcVR(StkM4m>B@Q)2ch zxDz#(fhs(><G~%-%pbk0Wx)#iC-?bBQFh_VD8F+@zp7>J<#+D*ojWlL9C+YPH2iyT zha(t{8aWe`!I60Ni*W-3cZx`nTwO>GlM%s#7sCPWEP`GxU(W!Y30e#~3&al<w1N_a z$^&;!g}5s6l(lnbLDhmgOHnI#7FIpr&JvV;(BKXYknjZ;+*t{M5ee>`W1HT97k=js zOj2gKXzWvc;Lh1;?;bvIhdm`ZmW)ntC)*Mp+<}wesLq``+3mVRYURP5O#dDy2HLkf zccSL<;7<Qv?$SfJv6$bv6FRO3cRaW=1-{^Xk;lS+9NyS*5F3442Xje2f(TPZe2b5j zjkP)%>%Y0f?-%mWGf5rh`I9=B^Ua-T_-~AoArzb8`<*-PHk*NgI~T&3iM6wakA@rE zS&odkd|d%LmxTbG54r$!5w8QBq7MP(fjjcWmm<Zpz#V=Js5)QEx&*aKF32i8GCg2k z%eok4A2hf_10;OG1$VB7z=#BQF0)N<zzYxVz$CGu-*gRQpXvj5UYqvr;RAQrQ<7uJ z=mdANEs?qS^*eW5*Et|}?qs*C2Y1l<ab)^8S3f*51%~0Ztf;vRRN;5-_?<h6?KyU( zP4DzOcgDjC_T$Nz45jwxSbL>nEGoemj^@sXlc9?X3nCp3-|8zHYjrY~doiPLCqp53 zTC>R_rqz~?vZ@%x;yEx4v~h?^!8cZ6*;uQSv7ycg#DPwl(}O$v=Yz`8@bAGLxFHX2 zrOEneGvfvZ?yNzI#M*h^fIF*@F_*9W>#(aqSAebrT?Kj_C{d_9a7VtxTSS^naOae& z1$Wv}D|c31Jv(=<LD>fl?$7`UUvR;lZU~G>aA!R#rkj2iLi%6__a9UiTkPi9wNLed zJL^z~gm(`gxWk^397{$gxRY&(%(bltcU;#wAi<sNcJ<&6hszx7hN~YQnH^vlz@4bM zJh<a`?)bl!MR4Q@P~xv?S%iY|<3S$WiT;X~2Y2+}lH*%f<^G(Ob)Ez7wXA6P_uvj% zkH^r0JJ&*Ea&;j&OqR@acEj;n)>`Q2^0gDR0kjdc3DgC;4wNWV9=Ov1aaH6gYv<0X zRSWKHL9N_bb@hNdn^E>bgF7@p!WUd{=N1TzNN{JHZF&#xz!W3$n>*K|S_$tSK5&OU zB{`OiPH-pN5}9jT5AL|Gb3lSS+3mW61BmjkWo7#J{s4EP=JMc<2X{QU<G~&I!}fmX z&gl44=+W@+!5x{N3^llO6SO8*7yAU<=|RR^zV?FpK>SPX#Aa>;-3&?;Di7S*3h{h! zXHnIHJFiF8+*x(?fIGLM?1Kh(Xn=$-xZuuP*!T@N*}tyN1*@K`evoW@nQCFcpUFJo zXaXfv9*BPmy@X=wruX0uOfe$Co!e2ZgewjoxWk^397{$gxRY&(%*C$<cU;#wAi<sN zcJ<&6N6?&&Ir{_LiJHrUJ09He;Eo4(Jh;QR*m_3?xDyTk9^8?e^H763Z-Un3>SCXO zJGUX@66g*PXI^gv{R!w!(3?StLgj%wZ-96{xO1AS)^G0Ig{rx;>goY^-ioph8r-1) z629PqJMUrRk3?|i9k%H`xC2v+NO0%vs8+(ehY#FgPf3m?qZ8c8wuA?F*wd=@gUZ?M z>cJh|u9^P5Kfs-+xjeYz!5t6ocyPyqJEI8PiH3g<?&!O-66i%W>#ITkwXAnRPjYo3 zIZTdaHyp2Jy$$-geEl=fPSD*T&OF`;dN(Lhs624zPa&QM?##npjcNsV-iN9s7i4v; zule)E47vgb%m<a<i=qu0+@S#yzTkp8AA`V%1b6PWE$hJ@m|{eNJKS29@b2LQci2<h zp~C!)gK+6fq{kJ|A6d*9Y-;2yj5z|el?3W43DnD9xz&#X(A7DjOh7@EdbON&9W+R# z89&t*W&TbBB944%_5_t}iOj{X-?`&@#sRr=C%auk59fym+<ItViGQWN#TC#VZa;KJ z7jp&bwAY!h)jY9h&tg{~(FK^JD=^0G0%?KzN&*L^`-q#xShtY8as~DWxDz#(2X{QU z<G~#d?s#x#6oEU@@ZT8To%B!aX6*l3R`kLNVVQw_Q28TBkz8F+i+zWM7sCPWd;og6 ze7z6!=b#UPc7Z+&`Y0$-s622-W(Gy1$@u2ZJT*^ME4cFs)XJTORnN|ykE85^26t$H zgfF<@&gUU8BEg*pY}0#i2c{U2;LiQ1R>Hf758Po-Nsc94NLpxy7p}kv1$VM7;lUko zh${V4=-KVML#pM!xs&PN`vcsGn#+Sb9^CQZjt6%<xHF2tooM)P)RT_z?7<yvh(Yh% zc?f!vtBZXC?tBUvbNTvd&|iQa1bqhdS<vS|i9+RpJD-HOD)Mv#Pk6>#DDr0pPdMgb zpHsDhJ6}Ng-St-WfIAPP?1Kh(Xn=$-xZuui2#iQ@=Pyw)-Si&Zfhk5LxbtOHE8*S4 z2kz`fX34Q+bb>qCmdITE`kgzj>l~0fce2~ngFEQ_I5Pcve}Fr?Q6TMA9^CQZjt6%< zxZ}Z{Q3UQp!@mc2a3|y;(94j$mi0|&Os+2W3ApnmWX$F3S3q9{eGT+=&^JJjfD(nu z19!d%@qBP+e$|3I-$K>gS#|Y*JCCC5g9dkKfP^o&;LZ=&_#^R~JKwcU@4+3IVnl*F z-$At!-aUNa4tq*+EE%2PPPQd77r!3dab4$t1b4FAbq5C!|JSk(EN~}kE)VW_aL0o? z9^CQZ&L{$RqT%0zJ2E{PYH(){v?f;<HOy&pEW6=&E$iFR&*kf1fxZWN9Q1wAUxS_i zB?^@X?mPx@Rpcpa=gxwv1$Ul8t=w64^?*B1qU?hPcW8iwFSy{&GY}Y&;LhLJruX0u zOfe$CogblE3GW_0aECo5IhKr0a3|Xm9^7G1tJV)HXSb^dcXYdE`uG0Wxf3;)2X{QU z<G~#d?s#x#6oEU@@bAGLeRoy@y$spToxg+D<mzIdfICkkV=iBR4EhP^Z$Uo={S5T? zphThaz?~mLJRjUyShe8JKcZ^M1)2VWV>n%9-CcnL=7Y-rfT9f=+@S#yzTkp8{|12( z3GO^^Th@a+FvW-jcb-GF65c(0;0}9=J5-F;4=QI{B6IQUckZ~Zb3pFg$!^#1Zs|X$ z%z;!-mXhr$G1*&t#-~5(oc^x4)}ha@)XQ&~@0z0(@Llsd6*ZR!cRaY`!5t6ocyMPF zfjiOg@4=n$##tl3&H8H4-`x2X^qdz^*+iaj%+>JGWPK$U!?APcS?K5T^`Ah$0R0m5 z&!B$+{VOO@s624z=MdNPm8_jRr&KMt^B<_1I}59xojd=IvJV>Ep#c)U;DS3ZvGGUZ zYgsSYruX0uOfe$Co!_Eb3GW_0aECo5IhGV?So4?nc;O0+&<`qSTf&1o;t*B(LFMdr z-67TTA5_lt@BOiJCu%Ma?s#y=gF7DF@!-xV0(YX}--A1P)}^7BA=|n0-_V*|T?l+M z+~Ce{kTI99zXSaq^q-&?LH`B%11M3bJaFgN5YGp9POVyS=YLT(cfD0T;LiV`?1Kh( zXn=$-xZuthEJ;TsxbsKb^d8)SDMlo?^M9yT!n=nL++j~ijwN8>2$#ORa0NyvxRY&( z%*C(Yx#N1q0l9N0yInoFBON_^W6u5nccSL<;Eo4(Jh<b*9S`n|B5)@f{u}kpDLnhX zmZc3b=$$+K!|BP@#XbRdUPcADe0_x#K_LpD8c;2$9+W6l9=P-WsG`VI*3O+ps#^bA z)>xRromE#4xN{K7K4@@<21xjV3+_yTz=#BQCfKI;;0`;tI!KMdIX3j0u3_vmR&nSm zgjmIHgze$>p>bK78p8zRU4cD&_AGWaa2^&b%M%btzodW$#4%pZ9dcHs1pxyFIzJd9 z3GW_0aECp`tp#hqBV79O!W9^y;7+zBJh&qcQKdU~vfDNEaQ|ACG;fh-x2Nw9a3^Xm z5AJwy$Adc_-0|SfC<1q);lEMtO6C-s;g?$6yOf@;#sreLF)jdN^KxFK=jG%AWsoz! z`-`KbHJf=k=LtGq-U-1~jjSzH#;zKw&~s^{0oN_MFwGLF5>O5EBlr@n1sKjt!8kVs zlk0`Zb|ywG+||Wo2qae*l7lHEFNWi_tntX0%hy9dhk_=88bFgkhk+7>$^&=CL0lC< znc&W8s#?LFBTzTjzpDq_IUHplG`K?pBz(aIccwvLM1ng<*`_z(h3@<oym;?>j%YEY zeZ&YBWR&QKpdPZU;D!wwS`4{fSDXIgf-6v0Nub^pV0U#jjHx6r0>Pal)80LN;0}9= zYx2<w?qply9{Q!enYgC%J9k`r9FRMAvfFisZdVWPWcm000C%G1^5BjKcRaY`!5t6o zj3RI+8vYyQu4GOz?8bvT5%@zSW?<k>P-b;OE%w<mFNOo$c{MWT^7UxYRM0V?V?oD( z8bOH#eIB^;Du}BhC<EMSR!yo}!JQLOH+NQDJ>bsqDEpwn9U36v3of|R1c4C=?##4J zZ!jAV?x2syhJMpEjD4za=gx^~?;bvIhdm`ZmVkw0hn$PNa0N!_&Yf&acyLEtp-Ok| zWVdVR;U3)4J)LK_r|%DNCu%Ma?s#y=gF7DF@!-xV0(YX}zfn&*!gFkf@4+1xi5VEU zGZz#3#M)V&`VTv}GXoiO`8o?U8*~zA4(MdiYe0!Y<$*iXA)XKJ%v040?lhxr?kuc& zz@2$0`=G%c8X(~dF1T|R1V$vdbBb+x173J=2OT{&^qa0>>{ET<&cd{J4<ES0o{}6( zz``-wz@2PMcyLD?qDsM?>~{6wj&9dX|K1<qPSji;-0|R!2X{QU<H4O#1nxw`f1|!R zh3D7|--A1Y0e8+Uv%09Ektc_i=EZRA+*yE(xqLkpv<P$>=ycE-pv9oXf<6!2nGbPQ zWO>VfbEg?E%2g}4!>8%V1zENHDfAYUebC?z4Uq5!7u-1?0wWUKS!$c!fEOOzK_8C| z{ibUe`&1vevn1`^!w2rLrzFP`uyBM+UtYKZBXs9Zwk0wbzy7r>*E0^t*RrzPHS}=5 zb4U7nk>_N4N=)__mxtj`p-0W-!5t6ocyPyqJ09E_Mc_^}{5R@*Pk4^a@IAQWA~6Ho zxpOWi^og~zBAzqs;7%Jd=JItJXgTO?&^e$Lpz}xpOJV;$dmgyc3h{h!XTGXdaOXnQ z&7Fl+&(56-Q1(HCJ2XJT7hG`XN(hWdaOV=+^ai}};0`)^Z0I*#!`P?#z@3ZJ-aUNa z4tq*+ECCD0Xaje$E#bi(afmA2xs%<l9^BFGn(5!;bo@RAccSL<;Eo4(Jh<b*9S`n| zB5)@f{u{%akN%0x@IAQWA~6F4cUG5KUF;KZXC*S`^7T^CD$r{|mw_$^T>(lg=<~px ziy)p4?krFT3b=EK_%C4(9O1{U!~I4AguEK;8K4EAHqc7Y)u3+BEugo6-UIp==<}f6 zpdWyq0sR~3B@q9_9KS@)4_5KLMs9y2O3Kbu25f2Ai8Sg6o^bFF5^-x#lW@0?g@aE# zhx6DibIZxGEFO~l4TmoIR7ys#R9khb#FxTp$b=6yh@?>oV}#ftZHl8L#<r;DnP3%Y ze#@a*Y8?)B)Hxi=*9b=!4j)}l#+SyWI1Nmu7Ry<c76uHMaxyMak%0Mz>vV(leT^(s z!c9!OB@!2=Q_g5-SP92CfN*fCX<{fYS1X*DTnPfDW=U;oJKwol;9S)^8sCIU(4tED zqKg^iv@fjA`2)60%Gr!TN9ikd4O*og)B#F3&M0oIlJ6qo9|P;%dhA$H*3Z_#zt)2~ z(|%UV5ziTT4ignW3Kdlx;p%17nXScfF1192Q63kXz!%Bbh=z!LGxAN5oUW2{vJb9> zO*Viwrft)-jVa=T^1Kx1a8cSWXI1)-)Lrw}!r>$%JiduvLuVl@^c#^f!@~2=w&U(p zYPqAN&eZ1mp-x;<PHUmWszEsBv0Rh;DCXO%pHqR8gnHbEGvusvWqzU#ZWqq07%7Kt z)k2T3=Y>4ZUX(gsU*U}M;6FqQILIxvlw8f=X`LUgXFPmEQU`K@D@kPxz6o6Ie#W~* z8MvEtPO1yPEX1|=O^lyEa9ZN)xHY#6$GG3s%&H+!YaPq4RUN8Vb>g#;^VC-*#@uSx zIjL77G3i<-H5m4|PGXL=*YQB9rCVZ6wZvw&<G7N2B56$WXU|=(?sYioCu-u%@l0cC zX42mva;E&vB4hM$rt(dP!&Hws92)3p;S8X?rx7wypW}(GOC3%J(tcswwNN7h_Odo9 zUv$@3O6_XpANNJe@q}ZH%)iat68^_O8tY~`*2zQ3AB3^bvDGtD!;_f^1r|}OTtD86 zDSttPA8>??Ny(&Hqs*yHPvwmauj(jbZNe0~D;OEZ>W*cc=lj23aeYX?!|(47qKMVN z@K~N4&Y{u{PwD!E)%sR=<(85ubt#Rja55ih;~FNbgz+-FrJ_c+RLuN1Dt5{#K599X z5ffXEQbDU!#d0X4|0Gj9cfHb!b9C5nm`dN+%pj{f9oRVZ{lUm0&qwvJ9^<s??iF#& zNEYX4-Eq#*qTg`X_EQ}W%XLhOqg&pjqRhEs1k+K}D!H61360kV;Z?eMug;Nno0e&{ zgZgEHHYd;fVwq6IR-Yu6D!;<%hVyO)Z2_GZP|*1B+$yf|V2kcoIN~yD=vFvA@UQDZ z+tPkE>Q^{hVUu1^U)nYY!U{((2tL}61wj|Qru>3nJF15N>IR(lazSt%)7u4szDvVy zst-$oZ8(qgB>`t_en|i$_$2|2;Fkonf|-K*B>{}FmrH`T&;H6;`w#jX)Ku-~3tAqj z(xTIQ_g~TTmIOE9Mtd{p7LZ>ONIT)=mjv1pL$@S&J^bqppxe@ZHpEMUtH1i?7DLjM zY)+vA^`XbDu*vP9Qrb5CSrX{E`turTq7EG9R!OW$#~X~cHpE4X6hoeRYGLHb(u1_Y zA7v(Q^*z72Df~m|MT(}<GxxmobK3vnEM3qdQ>W5LkXQNqnItbgYm=9MrSweerq>v` zu@BMMy!>hFy!6CM^U`y<l$V}q^U@Rd%}bBy-1J0=-SlqzS4vL=xqs=KsY)gNk9f0d zae9=-z&}KJq*&R=pS3*U$}JzI8+ei(M2rvQY8qKM0y#%dELc|V6N-8a!wUkQO`GpP zeB#!qGc_)%*UTU6$C{a2ne>`DtiFA^aR)MCIo^m9otRbL%eC{3)Psg}AEvERTd{K9 zjx}_1?!z>W6Qq6pFzo|h`DMQ!rn!&LIKA;}Wf;M)m1zXOR;Crq;DsU2KR(;bwKDCv z|5k1KHtLHmtstj;@#HUWt$ZhLtlYZ!W>8{6Fp3+?ua&ij4&7S$t?;kAK!2L{vmst9 zV}4v^8wB41o4gJ5_OxyKvsTtSN%RWCtRz0H+b^+_;3`Y6Bw!&PiIv3Dm+v~~u0#H? zv|YAy=*hcYNtkEqxeJlE9sb*?ThIB_F(!HWg~-tFVC?_%y=zhD^3V6)0o&uh`ZJvN za*a~{`QCbL_Tv-2F1+)Rze-t&>`D82l`{3+pX>K3C2z|gcS!j)3yk2`EHr{&v(O5D z%_7*`pyrY@Px^MXKHr;l($Z>uzPEeNo-_B~N-=NEayRbL_ki9B@@p23GCcg6McZNM z)-3Oaf4v9v-n5^MqBYA`v{}RVan0uTcfls_1HC_O8+Xm}G6dY0n#gJ1My*$es(&SO zg|qY(&RoF7kFwV#NC^F2Qkxqs?=(k(q8Dm)kzc28L18tF^as<$4~k4|1Q<S{$aBQm z)jB=F!AssL&YTqI<P@hV#aWQzEJ|_Ca5$)eQu3C==?v+kgm)@#M=<x7FJAmn-l2F# zl&I1e(PL^_gJ&jV9S)G!)+xf-7G}icKfdYlM}7h#3*nYnn0#r>kZ-~w|B+?Oi$*>S zi+qgV!ti9tQNPJ!Ud_;t{Mhnb)Yf9{#;~qN#ABQD5tey`NEwFBBT2@K_u!XlDa#tw zsyYd4pNV*sF*^7_sTTjJkLeg^c$8tem`?zzg#^QtWn3V?_2FYn{dln`yc*=J(=ol_ zoseh+{0ZC?XSTy(X$r!b%OE`AkS6|^wq=PAbvVp#Vu~{*#W~#Jur>`22eoT`Rf4mt zb#jVxSb~H1%N!1iJ=NiWp^i*(j&eAx#W4;ixKOF19S-#z>u{J?jblH`)Hxi=)H)o> zXzby`G$(AF_p;%25Gap5d;qGc{a&2tC42p{VKMQ1bS<8bZdY5>dORV`d)cr7nUcyQ z4<A|f-A9J!Wy94wpX)dBaHE*sJf3!+mXhm{2N=O44>SVDr0DkVUp5@oPfL#$XP7N# zRnmX7EPVH(B%`kV@QUWH<4BC5+}$tp=inxmn!M9QUKTi4dUj+6E4?Ac+<B>=^ML4H zjNDpe8fNd#<j8nuIpZNuic;}P<Mc)|7%g&Jv#H2&UtDGG?Vw1HQoCDj6pnends6d_ z)5+))uBpX#=jwX7isyWT!+B!Qp2a1H!;7J5j9tDl4xGmLh;NLcXbk;|_Q$2iU>+$Q z%RF=|G{|MFv#DZ=6Qnpz!kMO3Vh*Uk5yYN-CFn)8TUyIXNn<l^cP_*C$oI049C-#` zNNUM1q<*#iG05R4=^3|F1D71L6T^5xhB}QAFh%T;7&9muzPn)~0DI`aqvfj*dxXE= zT7e7ky?!T&ckum(o)67^_5nY%55>@aocPg;hs#emue?w$l==`%{9({XK_3Hs97I^| z6Zrik=u@EkLmv5`j)i%h7|is9){J(NN5He;a84OOMZ{rPZwYA)3#bC}7V9Z$I7K$; z3-w0Ya473)ECI)Lx1Te^g{fSN4u=;j9S$|Or#L++PRZd+WC;nP*g7*Dq&Q8&F>~8u zDt={()9$3`NyQThk8-V@snkw~Q{v4>77fOB&mz9T;c(O_%D7`>Akil9qOA}0G@zDI zUepsf9O`LGa2nczL|#f2Q=FYCPJ=9`qw*lH6sO7Iu-c1KoQIt9Q06J&*vZDT&ZR1f z27N2UBLp=^%?B-1CnMY(zLBw(W<X^NQoL3AN|kD85pxR(nc+{d@Vyv=82KxFO({G| zEmKTTHAp)QXAZli!XMW;mUIJKYaA$ZJ|&JmR~p0GMj4Zt-zdh1yah(F1sYj*fAgdj z;<pTc^Q1+1_?zd@+@1$@^W<$S5dVxj=|BM!YRShU{LPat?+<T*ep%pnO1)1eoXW2} zt;zh&)0o2FJcs7y>F-O%qx`S8Kt&6jK#BNFQ6u{nt5NN70U~Gt8R0p3D`QL}Pj=^l zT&OqcpOSv?Z-B39ntS9+vuo5tm~m>zQm2<F>>PM&!#xr#@$3o|l)zZ#@4Zq$f7@Db zi_sg;pcaSD=_(1E{SW537_nv^pM_`wbJl+!Aa0$2d&yjTiPG{Fl$$;__OXJWAJz)$ zoboI9xuLJ1cu!ctFAQr1bx!#ee0b<9I9<DpwBVP9wSqdQ{0e??=qorQR>8j<)(YyJ z@+<h|p|9YHu?l{5SSzS=%CF#8hQ5M$8pA_e^y|Y~L7h{61;4g;6>O9`oXGnN9&aoN z_%oX)9K1Df^yC1;K1erZOR}=b0UIpy{VAoE<Gtjqct5!>e2i!JJ2aek9_5-u+6?c! zXb_IMGpt1FF6=Sx!{eDiVvL5ufHf>gXsEBC!Q3NRhF+AS5406!am!KDPc4@qE#K5G zucfwv7IQznRIP)KZoKvFmZGjg`p`r1jW4#xI(3~2)Do#pAHH$56<k-};hFkovDwsI zIVb;ggWQ?zt(nc$D026O(%Pv^Ykw003Fj#?jh(n?S6HMIJ4L?$M%V({2t#aBfw~;$ z4%LHyaxGenqbxt=ZO8rkjGHH=T}^xUz^Zb8*{baBUsaZdRaq%E?~w|`JVFhmTE}iU z`pT1T4sJ+S>m<oVPt?K;VHP<Ov%fi-i*c)<zKtrllRHeZF#9{F;SQrDdvd20Js4)< zHuGa?+Z0k$IKB)NP3n+Bx$nSnRH}1iC|~bzm}-o}q0B)JXFXYRYz+rGy~#Da2dk!o zr_Y>m{FO7>^&6tFOoRF^6elgiyi{q2Xe#oo1r`W{)84T#o!VK_*OV?&V)g3awHIF4 zdcm@#!Rplsr8RCErgU?yQ-!$Fv;f{y$`?3P)#VEuBu%c~c?ZSx;g(@+r7Xr*5*SlS zpuUp8xJtD+xRNC&R1!F(lEB2U4B7+4QdtY1lO?u8TQj_`nj!Ez@QlO|2;afc9E$59 z(1^1a2NG{M6sr~4OXX~C+$NsBo5;ZZ6KmgtK+)+U)ZZw|&J9&QB$q*W<*vW}is_jo zbXK#^M!Su<QQR4(bw>s+`4WY5>&sMbec5T<5l?}o3t;LjrJ<lL(rb93^f_sv$0435 zM5Bn`A?Kv{_tWBe)z|eg#?W;!H>T${OgOi5-o*vCKF}W?A1zyeGDdTk^i4XMOh%u8 zHn#)oL0!cMbuFN`2y^JtcGT4`W_h*FrMyY^q-``$3KgzZOBx_Fc3>21)hIHL%GsFR z$gm<mNUPQ6H50Y7WpcAtZljzuy5@dh&93X8-1>LR(&pA$0-dqC1UF0m4ZUsZ226~) zIKM!~(ch=b)}s}CQvW1wwauD~R;g7#guY~}9Lf<l-71Yb8Cs=+Kuua8tLb!6%#6V8 zl24^=tRWuLxmX^(cPY*Z>d23Cbis`++)P;iM3*aqWsH&>LUi+(TPtV4ooHOlfYma! z0slI|>BH6eXs!<IL{CovPn-1iQ{HuU-HYj_Z)CRWJ9-EW2j^3Sr_^Uqrf+~A0X+)h z-=E_wi9_aJgPsIE4a%*GF8s7m<z?_E7I3Qy@G&MXD^PH#h`g)m?I)cx>CQP{c8f|0 zt!XBMIE=4zF%YO#GXLeYzBi$7_J?l!e9w>mw>L}Qa;<J4vuosbnYs|AXHDGFm!E4h z3tjq4!rb<90-Td``RQ<!^V~3J_U;aGm~b(doOC;_aqpsYXZ`J%o5#A=qlm6gTd1`a z4%wwPqb1IV_M5c}CKp(_Ieq;4b0#)TIqs&coQ(Uggnq7dHY1O|SU%nG1Kkn$(PFn% zF1YG}({4Nb(LPsqWE(vesYI&sx2GtTS(3J)=3IBJPoytDPDbgCCueo&q$ii}#MC#R zCwqHe81TxBMVtpDI;b{fGo>(xS2EK#;$$^A95!}6*=nu0hFSBP6pYjHGK<1h4W}eH zigHeVubWI6E$!^2AbO2HCeBMwVK=>U)2mW3FFoTbq|g0cJ%>s+eWLt`*ZCU@>%rC3 zk7?@?CB_DBaHA%4Zx=s-bfO9M5WQ9snW=)wpv?R2>TpUL0m)n@7KS5w<XwC7(52~A z;RSM5iQNKZYE)vn=rScZOG~S(z+D4+9q4M%Uw|G2{Rs3opdW+&7W7ll&p?UFsXM91 zl#RA*N^u$-&Ta??!ZGuudMOGvDesKy5}ogfb42XjJex@rz4+~09+_GE)E+%%I<YGe zyJLON)OXF&UsX}5*uw&d_`_SkTfkeuTfkeuTfkeuTfkeuTfkeuTfkdjU>3mgSl|Q$ zJ8)q;26PaJkU5`yj0f>CE&*?TkBE2{7xG-n63gMT_b|{D5Rm{blDXO^g2j({a<j?N zAbz5Wk3qN;=4<r<=#O|XF%7@RgH8ZV2k~JscN!7F;&zDHAU<@T1LD&@uABK}qzS|a zAI%_QR0N(Df=&SuUm%8X8t8P;8K5&ki$TN@_$kXGs1?)(S^`=MS_UF!dp77C&<fDG zpz}cIgDwDF2)YQw#rnmdOF(=)xC-=I5bJa~epiF809^^<mZCMF*MY7ET?1+db$}>O zT90riXai^?XcMRlbS>yQP&a5ZXbWg7s0VaCXd9>()CbxQx&d?}=qAw3pj$w<f?f}L z1L!soUY!Q)qSPIr9iTUY{seR<=uIG&#~<DT-U8kN-U8kN-U8kN-U8kN-U8kN-U1`X z0=pZzx<5_5G<#8ibzkYUfU8*Uqw2&OvIpzTZL;|*xpjVn!~|F~a|fT^8M7KI`k7c! zZ$}Efd#M-e)>-P*fGhW;PFLexta!I0zeT~HODVYa<sKZqnxgk$aaYdGSjz{p&qwdg z=?dePAT9TLZIwMjtqAe8i!S_0c9w<v4YNu{3}G{5S(>=cxQ<>R4yJT>N$I%quasx> z0to+nAUD+UO^WjNA#!2##d9{VkVG2hVjmD+F<BH~b&u8}#;_E+H=z965yOpo$-1!i ze4hh5h=5Ua0vG6mEL$zxF2rm$L#|ui0oeizFyB7p3zPx*64SU8*IF<4Oi}w#<UGaZ z9Ob!I@+5AP&ht`~VKY33`?I#?)aY2G?oG(*95$g=?UDl#pmYwV74(ZdmGU9_#SN3& zP$I2=9rOdcfz{|YY5m--$K7rE6{s6fj{x}6THrpqUuCxv^Xdsx>%8b0z>km@>#_{3 zLaiOpCNDLih1lww5;<h~B@sLJ%JtB|-Z3w%5px0Rgj_WCmGlGFs1sP-a^z00(Y}A4 z{IVx>=d=?&@4A?zuGe+Q5jYrfB>I;0c>UVe7U<PIEs$NtTTpK1oY(VcK}t|o=d=wu z0bxT<%MllUq01ey0$_c7*^GJiVPwj(;WTJzg-3AfY^0523VYOc<OW<Wv}AwxZT2tc z6Oi)?)T|ffV?X7_-V4y5x5e_-t*FPdO(Mm2zO?<^eVpZbK~|mVH?*qOz<Y?UEqlUx zsqN{INbj{Y{wjMcGsa5RDoPno5slKi2P}dlcTe`AZ}IIl?g?fez6rd%v5&ZOJe`{= zYB!95Xty&%>2Qx`VppmjEwb$NYNTC@(&)62jcyUm?7>;PH}zN>e@CzUorU+eNJo4_ zcYHgysS|_;coYLzxzrx8A8}ShESU#?+XDJQ)K-!AH>+Y$wCNzE&YU&o%3~+AHSxu; zwt#J%KVK*Y98q<?1xR1<g}WL{Z+&LEPEUW%nmy1L;&=K0{USblV5~cPC<b^E5av|_ z%`;B<``7*b*e@T?jiGHax9Q)#yBNUhW41adfHEAlJE@2Fr#ATIE#jS=D?~5%7K2lq zlw&2GenQh09EGcLRxSpZSA_Z1G6fBVBbdPa=zA+M0<n*FT5A=9xn<MvMU}Q-GSXZG zYxTind5%lhA+AXC;ApVY@qSbc;EiE<C~fv&mKvy=_kBG((Y>`7+UGd(?9V(N5_z0t zJmrYL1+tNC>k(uA3I;bDYuj@4z6n-s##povzxqDB4Jn(QT*4~I<E}RG>*O6f?z{B) z5^5_4?5lm3dAU9oAE%P7IiC-&cu0D-<^@RIjoNi!?C!*KC(dMx9BD%9F;@ud1CQj| zf&k(4UD5mjsdyck*3^+YbRGHnG+&@L?;XVLIR;WSxBc0qyN>_OD+lj1_DsFDjptog z;8|DP>zHzKfD?`hl9FDx48CK=MUy6KJ(jN_U<y2Kw7I!GL*MVIqZq7otWqCHLN!)W z!vEpp?(5~R!Q8-oRs1zFLh9GP<37idELi(yQHWe%sRo>vqV${b?`Guf_R2r5e&1~` z9P`y<UVxbHm8J#b?=2LA*-nW~JL<ML6gfm~5c&T>;0GWcEQK!1D%4~f%EP{b62`pa zmEd0Z1!Q+pmUf^|awec>L$nHe4%Z9i?(A`I<7o46!$rRkeK=`vGaj9uTgGe1=f{QP z@8;u3wM*>68n%YBgCbX(VX3(eHMZd6<u=|~%J?*AQh77IV$f!FvIRGGT)upH=c+M$ z%-t5ufX+)`gDseS(GttV6V1Is&yM20PYsg-B*IZIwoH%Hv>(@G97~ouc_06$OMk<Z z{K`&Sz<Gt6_ZB>><@%YQJuqcRjxLwHof>e!IC^xs5Eh{HdiX8p(meOcD8crPkI5|k z1Ap23_UnK1`<=S}9Kqx77sY*;y;X%DCeZgJjz!U3h+f<!J`}&}GnM&Rgtm~r>l^=? z4-d;qXFd@zSuE?OTi~RN#y;j}J_?~nqX3*_FOTQxrZt~}(Bl!)CdY$#+Ng&!U-KCV zJucBY(MS|e<>qKU_Ap6m$sD7ZP_p+GgY&HianFT$<4d<kQz9+<bbJozey5>GbRg_q zyhWLJxU?>m5muw`^+3x;ko|Oqj1_r$qaMaV#=OO)J#+>*^m2Fg;FdJr?`c=_cGg6( z5c_?6w2I#Oa!0D9&U0;TImX04HPFUc4QiWww@de2-bc7%XAfS2v8fnLvzjmvDf6aQ zI~t@dV0n`gZ(g@`!mZ<vnxo6FC8Cj_0V%cylWw@F{oz2{o+H7fScS7Z?8g@%Z)1Jk z-)@vK9piRM&vB`v`PH}GT3?*MLhIn2ly-LCrH#*3G4)aAtuCDg_Y?8h$}e~p1E4Ko z-W)~j$c&94ZL|+|$Pl$4>cdj-HBr_@uQKCz52l{nweVF)qIRMhZ9#w#(?nx%^qMK} zWKo~vJ;B@*o|3e>&(U)0VY3^NJH4O>siHo|9AED1J>(rVuUxE0J?0pBkxmo&cXGUc z_re!wsBpyPcptt0nH*~&7_-te8iohVrSH(u+Dk{I&W+GtYG~xR{NkH`_U21&d;K@r z)<Gk^NHv~dV<(f1qX`r=qCc13a+sEhG`#KM2kxOvyT~MCyWjBNs~<b#$%}Y*HTqe8 zo%`~hMH`;Kc=a?+a)QPw;0FcvqZd&ZIU08qjO?+8d~xZ=dcQPZ%i@i&(#`@}%^t^1 zee^LV<KK}ZG2+QT+**9@mSftqx?M%`?=4(t(_l|nm<m~Ad)BUERw~h0ymuLYsZDD< zXUW|KwiS={AaccjL>vF+{1J>w9Pcb(<|!*zt))~{#i`u<B|Lgkjih*?l|QB~48KeC z=~TRyEoJJ@#b$>4o`NocQt!5*_uA0=3vBgxZ9X7zO#MMCb)OBX#x1tBK5@x!Z(ydn z1ug?$S-A!qD%ntALrq}_f2+juR@zXzgi2>YbtCq|8Q(bM{CS^lIB4p#;u|M3x0Rfc zsyh8s&_6ZucUNlVT(AEI3x)EImC{D6al4w${A70^bsHNpwJbdOtG{eOR(SmJ74BEm zZDe@-C8y@cPyG1axkop=^Sxu$Npt?^GfTm%Pw+m4$?bYj9pacHdl!xp??LEestDpe zZfC4G)cp_=9ERU<_?-Z&;J=idzh}$X#_^DA3jF~QuD|J>9JMb%yvATTe<wCaJXFh3 zH|5OS!i?0eq?V<XD2qOfc`Q+V=E-r5@2qn*NL)#evK*H+>ZQ}NI!w#*x>&Q0=Ng*v z7zbp8jjv`+S&fdVka?}#hj=$2rm!9JiIYR@QR}0pQ=hqOnX#Yc<jkW3akLR_eJl#q zj9=DkKKe58Z;tX@y<aAE<mbXTXXewOd4fteL6$aqEx79t$0q{3AN7XiLX?^YYD3wG z*{*le&PBTu6*!e*vQJg#!+SE(kzSE*R;MYBavVpNqdZZ}LbVA0crebjC(Fp%Xj|&L zKHEj>WVvZ$&X6}^M$i}IQ`ZvY!?`|vDN3R9IS2f1X=6V3U_SGaLfiRL@O26K^)Ajc zdYtrZkI1K(mOEz^FjpdH8ogMLU8)T0?|!Wh`E&7Hq6cNw`+(OXo->dp*@>(DiK`Ii z*cWnn7hzs{rK)W~zvBvwl57uhDHs2v*SM56rS1U!q<uPi(#3d|Hy<f9sIc~@{y*!} zV?qCry}76hSAe=*$~`w@Sy^Wtr{()_O`pwrDupP4$dXT!44)p(=SETPx_^~_Vx!Ae z?#YVLr{{0ca}e^`XItUvSx+x06M*ucGw5<rzg`v47j&uDh3TWYxxp2*jDGH__lC!@ z#PXQD?n`MzUAKKS7pshe(;xMiMkMw^l$*V9v5Z#w2_1VT(bKi43wv`XQYhZT&}a4d zLxhw)H=h1%So2c!_EwCOJUAv@0;w(18z_4@+H^fqZbELSU{5<o2wi_yj=kTE8H*ve zNF-;Z^tGaF9B=4Fi^H)}>tx^A8JHQ$vwWtDSp-K0v}r5(1qSQbFYO(<W-fU$I}2JF zq73g^9KCrLb&gujL3agnFhf)_v!HZCVXD1;My^d60xM%?DYyd*k6M$n6zb}*DSB+E zWK-<4Ays4YXt1F`LZA5H(leSQ^z(}jIjbn4mtOh##FY}-jg?P(4J}}f9^o_Zl7z@_ zs5RPm*09DqYdNNKe2a}%wdnt$6_}=}mg9T!_;?^v;mj498qcpasc7Z6?fA)}hB}jT zU_%Y{<gq=9!ed#Rgogk$u!KR`*|4#7sb!_iV=2$0-P%@LnLKSr>JCIVL)vBxwTo?7 z;-tM8yHjGN<koVL4FmC-QW#Te*=c2ztz$9DGgh;p#@K3aZNvV<abgqNPTvPP1K_+v z?}*`iL)(tMhi`kVs8i}cP^qNtp2tf0Zmb`w!*(TUVb1z?7M5B+ExaF|BQcv0vr<BA z)Aj<rw845s{F?DQ3kxD04;USMmqqIk4~gl336bW6GHI@r7<!8MO<d+~qnGc-?6hUs zS-?Ft>}Rz$Z_dLX3VIOJAbpGXwZQhWCJ8Zzq7CVl30SIKcw7_p*c3`iztdi8TTnW% zC24B>g_K11H=Y{`PusFlR@o6mdp1j;YCj_~1WY&^{`t%Q_3{h9`q#gD`K90f{=SC7 z;b0LM{Ku!CeDZrwJpMON|LDG;U}QgY|NRd=_}P!&fB$D6y#Kx?8Orbe;>?Msf48^r zub(*YXT`#iNJ&WRXD|QzOW*j%mw)&2E5Ci|H!t0{vOuheWBlJe^MfBg^Td7aR^@{a zKKKwwj{AB<<vpL9Gx79q-&+6ckDd1qrC{Yme6nXITS$&s4DqhMY8GpJqQvkiz)Txz zXfo|N$%ZD|&|wln0yRZK8#ZicIoyVhu%TB;=!re&x4hbhj<lhpBs72i{Fb9_XsQhz zBOzM*SQ|Rdh5`w(1k)_<e`mgl11b(_BMA|uh#&dawC>&<K0jkM*S_AK#xCyfNcNY8 zS(l-*tRaU$43h)ufs&-mX_D2-d)L6VCQG5Sm@HZurl<aA-NKgN*M0G<>%Tqq5riKx zVaEUT;n%fbqJaC0Ic|CE?<cN6-ELW8IOexW##dg=lMu()W*bs6#&TWRFw<D7zjZ2Z zfApL0xP6IO^!B&Bm5+x~$0VzjckGy@O1zr;jWZqw?-7O>$7^aWo%sCM$(gbG&1JPt zwpuBrwJ=0YrAK%Tny>k#mR~<O?!;YBwLMZv>tUjmdT9SYth~PEm$$7w>zAtZ&9DFJ zfp32DpO0%f{p$-J8K-v5|G?K;T5kOCtd^%=IOdUOr|drK`Df04l=feHbFr1_<3~~d znL$nzG~(NG!%Vh;viFQ8X#-5n)#(zV{ZEk)#flQ52frvGwEHW|ZETI5PZT6XxjM_! zcs6;pHn!fz_OzLMaF5NS!)o3+ldBD4%_QDaNxRB&KW9^Y(Q10shQ1>q%>UHm5(*G% zYBDP%&LE!<J_2f$waOYBS{;V4uCl!KHnb@Wm0C(gQy(QE=2#4Q=y9!j9U8wMYqWy) zNgj35YR~?zc{ho%V<n#_x@WgGwCa3HEv>1xy;0h>U`sboYR0xbLqcrZQ*CWqCB*#y zOG0ehvu*5P+V*<ew%6M{)>_SXmu<xlm*sxmruvfA^er3uu7qgm?@K5^Xr9xyKM+3H z>b2G?ud|^m!cakVSYD?Mb%mi)%N@3D3$|@rZQB+G)VB4~YTx+D!rdg6Y@Fo7ww+|# zwk_4RH%Z&p*wQsi&DgeQN{DT{$kw(^LTuarmJr+a92+~Bw%umicAL#(oz;9#*;f2W zS?<F&)t9ZN$86}YB*eD;YY7DiH9Kwlr0~I3mszV^Z9`Xvp@Ld#c^ho#+AvgV*<stZ z#<p#nZQGgwwe6Tx+m4rf*tU~x+b&79?ak7*wYGHgZOs--h;4hC<t>pA+x8C<V%x5; zv4d&bUfZ_4HjnjI^E=D7;z!GJzhG1SrPcIp8~UDv*tUBl6d*L;Y1^lS54O78TICuW zx+)A6)H=)CXhYY9p;F5mZQIt`wq0V|wst^mdyr{0&KG3PaIoa_#{c<9%VD-{16#)# z5?T)Ibo$I`NWwNG@s1NN%@CTtl-G-z+Hf`p+tb_?i0j5CT1LeF8o4Q93GhXeR*i_N z76@rtV(xaF<*4N|Bz4frv?e`SGePEV8!YEtX6xiPo}M3iN-&Cgc`lIDN1b|Jz>xM! zJ{i@s0rK=*fxHIA{7NuTAfacWtVu#F^Sm$wY@=}v7zXvF4%CPwn5Qj4?KDP(2e~{5 zB-lfcIeMTHJdXJ1uEt*wyF7~4`lqXAoO#UQPn=eBl+2cd_w~9(=bkWT>FIG^_xb<4 zNG*Bz*qWoDtJHBUYauH=Npasv#kr*^b2h|NOgUv%VMTGyia76gh0d#9p7Nemao#Ua ze{1XJ3Gcoj&YRTx#I}hy{=b=V-v2G%)b;6`?rn?nj$Zt;#Yb*A=g>H>{nkI++A;31 zBjUVk{_Vl8HFKA>*Bo^&5blCninDGf+UzJ5`3=8_6Th13Tpbf#1?zSn8(kHzDIOPH z1?!4IbcGxmV^=S<Op9Id3p?@rPH?WKJ6AKJtJ3n~iO$u`=&E>oah7v6JGw$yPI9j1 z#I9a=<mA{DzvmN`?1h%s#I9&G#=ZN^k3Lj)ZgUW~_1|1~&axSIee}*a?^okK_=W{f zAJP%$Ef4oD)EqUCeG8K9Ehn8^|GC><dtF>z=cj+T^b5D0J2%c-@r8FZpN*Ff;=D)4 z?>_mk*L-MAoM%219_Ri0<6mm&yZ9U1;=KEA`c}s^HIHqM^R}M%<@PJazHL^V*D?Fw zzjx<%AG<5gd*+!>U;fN9^;g7s&yHO;v-RGltKz)>Jag*l|MJDfud6wV{k?!k+i_Rb z!v_G**2BV~;+(cAx+5O0e~!>UCE2HR5O?nbSvBo=EZlu;uu6ERqx}%z$Qe8Gui4}E zxP?)qRgYWjLH_U-@D}hE@D>;<3uNt|<7oTroge!B|6H`G;nR0x|J;cW|EvkTzH61- z5E#E&7oyaT-{V1>KodZ(2XVgm77(9cJp|&qnjqkG5T1(3cLiU>FP}RpD8oe6*;988 zu3s$NaLyYG2VeB{`c-fH&gXxyy9i#xulwG+aOUH()05BexO*&bKL*q6-1WY14I~Zq zvb=oTc^-awO}vw9hqFQ5c#z381y>#1$go^~@r<kXGYb$@AF<~p69cseoaK<>VSZ;K zT#{>Q;GQ@f&^j5gHpv`s@Q1g6w}7{Rw}7{RSO9+>E&hK<tn%E>)z$caNb2sDRF?(R zy{ftj>`Ut{;4R=S;4R=SFaj*_82*HSKfDFJ1-u2k1-u2k1-u2k1-u2k1-u2k1-u2k z1-u2k1-u2k1xAnsxY=o+A0x<qds7wITPL%D^relmfxXG;4R6rNan^x&MhWKL=^tUK z(pEwR<IJ4;rp3~noa8=ATVU`;>!*f(ijC}cDQkoT6PF22MVnZ88>pCMln4yeAPM=z z<z5&qOG6faFS4lKB-RKvkf_T*`HAGC^AbkOvRlF+39+}h2ZR#>Ho4|!#agj+U@wxS zl~gGwzwU_57f9FI%G*F%CX2HdNwQRpFn<3nQ&zGpbA*{_n5r{S(+yNUUTmZtO!By@ zY6<7pH$S#)9c>aDC@VJFn6O4a4N_4oOCZiiIDVOL?fy&V>#3I5e%9BD*?_KK;<8$5 zz2D0TyB+w)ez(%TDF7N`KpExB=g0Olqajh!&%6#au_3SfKot&D{y;O}-j!#_Yn@h@ z;3O_}vCA^|u0~aq3boc1PoGf>i5%D%RkrS0xm6ffYop30^h|F7Zvk%sZvk%sZvk%s zZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%sZvk%s zZvk%sZvk%sZvk(C1JDBe3rqg+7VsAE7Vs7r4hvkOI@K1n9_MXBfm))v@i+B)@oxqG zx?ZbVEPn%!f3dETY#U_xHv(6dmAw#JHzSY5(Auds;A%T^>QjNbRIS6+7F>1V+zC!H zC#6n+&Q`>BK}QEtb*ZhmV%k1jZ^K#V9c0KYgY^2COgx7Gf0vJS-iSIcR`ccW{4qb4 zi`HR@;t~thsi>!x<KH=q>skg)ol?&&C=>PZF9<5dKM82=W&y$l5c9xMnxlVHe*-aO k`yX!sZvk%sZvk%sZvk%sZvk%sZvk%sZvk(CL0jPe1KVD{>Hq)$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_d.png new file mode 100644 index 0000000000000000000000000000000000000000..081e06e98dde66574339e3833f05cfd50a0191d5 GIT binary patch literal 899 zcmV-}1AP36P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#B}qg<RCwCFmS0FyQ5?s=yW73<PfnMh z`C#3tbJ!*cTJ}&-L4`r&=&9F&f+(Uue2nPTAcD*wA`l|zVNmpvrh$r043Tn@BfX%` zto?D*+#k2Q&N<WcK5TAn>j#H>clW#JbHBfH&hPhAb$53+0kDxFA`hlgsi0P?q0wj{ z@Pfn(yj4Sig<3(Zt*wJzuLs9*$d`G&9?|Xf*(j6&Esc#R(dpP1<;$YcDEj;QL?(+W z%r}r4ouB7<FenXazJXgNXtm(*pN%KtvXW<e`37Sn<8V49ghF9Fcyb>mbA9UFOrx3! zWZAWh_iqO<{%HbJUuW1ev&D$^i)}DBH9&ICL97$WQmNT7NxHV_z<Ro#VPwdTCD$*M z>2;_xinwv-3feE7$8i5JzDzjq{BbYyZ9RDuT0sk~R+aSlr3D=z6B`NWX2&%gIo1Nl z<TOS<+Hw1GC&H14!Z#AxfSM!8fY)RP)$2=%!cr_q^LX8BLm;r0)K`{eb{=EV7~3@e z45br%oBqzmmWetZ2M)I&xE{jf#1z7zgyMKS4mmEfYgTI-BVWJMF09Pp_})V3uXd3T zs$m}*L(jt(=_iSFoMbun_udfkL6jEZt~P-42iPucY_=$TasrLTwv`$dop6!x?Fnsw z^LW|1t^W=xd~%~Swyk2#=SKu86gU<jcswk9j(!+Z_~Zl{-JxQ(ScjrjKLlS0<-Qfn zI_6*>{;crHd3fim4x*^|2Q*|27kb~p{nM9uwaGNRG6}7}q$=@a$*u6y76>_1DyI%z zy~zLz$-QT-XB2HpR#20v1`dZ)@vO449L=qVaO&I%l$GllM73E(=rPI~+Hvm|I<MbF zFcd<A*@!mlX;jzjXa8{&#y|;}hBaY4t29ZW)Ok0n*jIt4uO1O-kfggXNpo=q<0yzr zbb4x9&e(lIWvT{`_@q}->13pZ+}v0)Q$<1a{REs#yOY$AJ~S;yI-x*(DU2rbOhF1L zDY%kv#V68ov|Dq=)TQVMcC#gyi(Vw6RIvI3tG2<x0g)%+6G)4wW}85jkG@PI!}>>n Z0RWt)4oZGdhCu)T002ovPDHLkV1mI+rUC!} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_h.png new file mode 100644 index 0000000000000000000000000000000000000000..88795fd449340ef83f256d3950cf9b63a9fd5047 GIT binary patch literal 1092 zcmV-K1iSl*P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#=1D|BRCwCFmd|e+MHI*1?9K)|PMmcb z>Qr1>S{%1X*ewDfRpN#~6v_b(xp6>3f<ym--jMhUxBx;zr~-m24izAf@&_OhN?d_M zN}WPPOJd^2>#+Wn?7YXkUE6i2#|;N}q>)y;Z{BC$d*698qOiO?3jp(ax-k0T>A4^o zm`<nNnRI&O#7epBmdoXNiG*(I&><idj2z0Gjm-^L5&<ZsfF3|@QBUS+C;_4Z5Z36S zgupyWh=5AtE&N{&KKta0?CQ$uT&=c~ZMWL+)`d4e$enq@8wy1N=1F9zjuh|1+_=3N z`|g{??9I~6xyn{0%kLh`oPe{>o`uZZiPp+5zdm|=?s!4klBBfB4AxN1){!bB?EUvX zdS<Qkd#+Kh&rT*M;pnlWaPE~C;e~V0!P56X!u8eP;PU5R#<c0nr=QLh$H&Kmcs!;A zh=Kl?7e=b|6b9&@>$jfgh!~Q8G6y%Vmtc9R0PkFU7k1mb`)qx`E`_j2+BQll>0V$b zQqHpq0uW*_)=f=8D&@k)Uz_mtm2aW$`^K#7?(K<Yt7+-<TI2EfJ?V=?8w!brMydh@ zYd6-y*&Wjp4?!lM2NqEA=apW>K@bR@VfF@;j3Z^9ZVoC`l&SIx_b<-M9XNAj7EZr* z9xTg(!qsbV>Elm(B4Jo0PBd&A6-62-1j`UAfA5Zj@?qNcq1N05uT}%CRLGpl^lTW! z&n76Fg^_Y3q^C*0I$d|`8&sjf?XuAVuj+vo3$%2KyEfDjB}c0aJA!$UkcQN>|Brot z2imp;{vYdb%&UPdZ73{V>)9Yhn;$#T4puJ<i7?uy2KB1G#anF)9BhMZMCrY~ehaSt z@KevGn^tZ%YEVu2rwF5(Vpx;GQtEFUD)|UB|G5J~FI@a;5w`Da|EK<V7p%~zkzN}# z4YhBv?lTiJQPh7|_SxxFw?iRO1T^`KngbgV0UV&FgGO>HSvoxX@LK+<Trt##iu{>; zVwhf{p^lSNldIKjFWdi4_tvfSk#zC#{K;Zsa-wYsLEO>wzSYfMq6jl>NH2AJ?~sOn z{?(VREL^&r(%T}InogBY<xVaiPS5P{H_Nuk)*zi)bfkl}?zv-Q4sQiA`HW7P&KYMK zZYtS&<L%c!Y&4oqquJ!26daLl^OQK|IH+4K+B!npvWdTmj;PE+8j}C+(2me?oFGP= z6%-Bw?C>2jBm{M~93Nf7+%Zc6;eExzL!TTP5Z`0BT~L3Whz@wmqR!EF5Gb2lmLTZ# zt^=+acxB;xujl)|n@A)^ele@p>*0l)g|XmOtMht3up9or0t^7bE1IoL6NtJ10000< KMNUMnLSTX^mlvl1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/materialSelectorIcon_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a563b88d2fd5a60693f383476d7bbe1f48e74607 GIT binary patch literal 697 zcmV;q0!ICbP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!TS-JgRCwC#R$Xh;U=)7d^kY><yD{(E zV&|$C#ds}4*ot^%|AF`q{2L<T4-hYed7*V<c+oj*Z$xjzp__Iv+;AWDVwJXQnr2PS zhbJLv6Ok2?OfU2e59iIBlRSA&&N+FRuIu>IgTr4K|4C1Eghx4KQt98W&(6&k$lQ#@ zVufC>XDsorE9G6M&-U(DZ=?>A2{I;;Ws#Y_fLtzz>CE|~#igZrB7IEDDnaw5l*sOo zxkb1fk0+2!CBgGNuFYP>#^)-ww!WbFe9?HJhlC=*kliQfo=GPrCLbhIDdh8WxRjm2 z{y_~NKCa{Lz59@5`B(QbOQ~X6Z=~@=9En5%hlh2PN-GdW@g(;UAz~@@PO7RZc6PQ+ z4CF+|z+d4_rJL1F$o+w0W>p09^-VR^ofDC01T)zzZd}iU<2YQT`NG1JVCz)VwE;R} z2hnI0Vpl}BE1@e%NK<0j*8{6lMNx*%;V=ZDgYoeW+Ja!78zFZ31VXB5Lj}juG5T^J z`+Iw6w*`bkA*^qFa`^;8s%yh?GcuRs7$m8Sqjnp$+BdANy?6NpLaJ)2t9+V`hB+^n zD`++wr_*~?Wk|~Cn3_6g&cD~|E<eVY5L|diWhG-TImWH7yjXtbyv?6dRa@4f7hHA5 zxGH_yVyo49`tI!;!6MPEMz8oLC8f7%AC;!?O_Hir3iqSRtI~m;+!pM9Yv9L8Ezk_k zsEwKRKQ&(NEa8mRP;4UAB2gUYwrb!1Q{!Q>qp2$n*lP8_63LMmcG#E~B2PO92a)f= f{+Z48PXPu117{CBW|M@b00000NkvXXu0mjfq6|rG literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..81aa03f2d0a4c2cf9651fb013f30a491faeb5b02 GIT binary patch literal 915 zcmV;E18n?>P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#HAzH4RCwCFmRm@aK@`XT`(2gV)n!SA zFuuGkrjk@>M1d9=N|8(S&}%&e(S-yNdMKi|u$Lf9h+c#UdRVHLP|--O6w4wn>1J(d zUdXg8+>5*4HfL7LMa|vT^}ylVof+mg|1&dZj-jTeCLe&ExrP{+!C-(WijX7;imW(g zMYc%<j8_#cJv{?vvl)UQAXYXx`AwUcoU#*?19S88U^JQdiehCxpAU@<4H}n43S$i< zqxof728U8eV-1`)MO7hSjZJ3a)*Nb}s75=9{q93_v~|MaaKi2O;M&bAu;yg_dv7C! zjE{_?{!tS;Uv#0Tdw{p)<YuD$NGWpiv*C1ngqESPGa~Z)85Q(a^=-7Zv}1JSD-z8n zWMpbMU2y{C#}1*ju@!H-dU5AQ9Uog*yc4RTLRAe*-utkjO>E*!20BxD5(T?=p!ZE5 zo<C{F`Qugac)cNGz1~@f0?R_O6dlxTPGAZXFzg({{W?3Qr+@J_@9Zr6em!KYuIoGz z_<eqUTK^kL6ZH2DawkhmOGVa}47`5X4Wn^AK7aZGk0+=&5C}jI=={5sg?(lGZ1wYQ zG_AwFgT+W?U;?FVOh&=3ZHS9kdH+flHdNRBfBf*#G6eJhcaSOuBByXu|EfW0-{yUE zc#MaRoc_P>|G*ZY!(vHXw7=0Q_51%i1CYwc4;ru`$%42z6^?g<5ZJw>{uL8{*w4}e z1v4`)G}k{x0t;uw#k0Jv=4vel7}&R(N*9K=<Jl{?X51($*~9x<*c~2ZX>e$0EUfTK zk%V{NCMKr1_gtu6G)|3ERu8(;Rmie#MlQ>}2W%xF^-)w%t2GV1y^fH!<YWuB7jDJg zvLYl}%p78vd%h^yFAEjL@V2$`@;OwUx&)WojqIFEl-l;Q<gh7tFbKv$L72p=KbgNY zOP4KTcw&=Ml2Ci^I)l34Veh8S?kK?o0tvwqQ>5pN{|>5LuCkO^9U4JlB~tA=cd&G> zia@mRAY2Hmgq7I4=bZXJrO~UfQgKETDr}Jiqihrjh-`FZgg#EDRI&Y-{%LP+Zqj6? pLSZGM$oIk!ixSDn-1b|50RY7ROHE(~=e_^{002ovPDHLkV1n~Lsv7_R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5ccbd98352d21dbbedb6c9af9499a5fa02428208 GIT binary patch literal 1098 zcmV-Q1hxB#P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#>`6pHRCwCFmfvd>R}{z3+_@V^caxnp zn{1?&8k--hVm4F?rP9_1`%?w!Q{DtY1nGa!w}O9y4+`x=Td7ig5Tu1d1!+NArOAV< ziP_y)h{nYqyUNb~$j-gD=iHf<-E?-67S;=gC6hb%Gv9N*_uOmD&CZShzzl{By&q+a z3!H$dRLV}KQavX=DHiQwu{gt}l6GQn5R`Cx4kgaYqZOMg1t5d~^$Kz;F^Gpy1yTwC zgY_P&BoL1)DM1RvGueBnR6sl~35rVP|6pGH{!)73?!x5r=P%NYdIL^=apLUW={@Ce zFhY@yO2vjCD-R!8zx?!TI-e^{K7H~uP0z+B#$f;Z`(a{ote(AnXXl#}qjQ{d#Z5EN zCdAW91%?p<JA3AvcM7>ers`Bj`Um=9$LJ0?eDn}}a_B?2dE*u=%-@3_f4FSn)FYXF zQ`u;Ai@VvfgkczKfWA`7vv6HlJE46n|GoV2_{6w|Oz+FU;zAB)Z_dG2U!I25#%jl0 z$EkA0m@*lYq__jBO{<Nh)>UdJ;*dz#lwP=c<!5jz6|n6%G@DI)x3e_O=BiPv)memd zhC;+zrNqPLAhe^x_Nj0M<QMZgXl#FDJM0`Ag*&(JLM#@82g?s$DsH;2!QE9uXmiSd zZ6kmY*%cBz7Y7F|(28g*3hy0w7hZdPn+6RH55e?X)3C|f1T7G)8MF>@v{IU=Xu%Mr zG_f^4`t+c#ORGjod1~VEIHujvS)+1MmlFZIf_O~&H4RhhqS0DWEI#&O_#S@wQsKV- zwXx>(f2q6%AOqA2|2d~G)%!HWy&bjjf!DA727}412y|g7zXYjmsWlLxA)P7nGQ++6 zE8kmPjMVmWtyY5@zyARP@c}q->bQRP?bqj_kjv|NsvRt3V5wfV!O+&#R-3<W&7pac zaNytv`tLunIo!t-({^fQ#mipsx?Xdj2`c<x6K!8ydREeLZ+`J#^GwrhXpacLegW%B zonEB^_fyxJ1`JGfZg^xUpLu&KOTD#aw4nF&(I$O+hcDVcUwT$fw?DHsTaOWx-ILj! zjmEY#sP$>J)B;UYqAyj@y51?{pK)R1$mgG3x%l0sge!!V*lOo?Pwkpb4kusuDOCjI z+@xMBTfbVc>}DiN!#XvMC*t+vr;eR-s?|unR>y`S4N{~VI|3pRgqa?UJ_36#MVFc) z6qH=IW;75W`yzd=@{D2-h)$A7Uqmwh1KR7Rx(QMK(Iz)|ZD1yomSr_*LB<TlNYw_- z)_!H7dvCc?so2qIbbTnM4NOCIbeR8ey>T2zU%1J=7s{p548|+F;r}DR0GrJB+0&Ql QL;wH)07*qoM6N<$f-YDQ_5c6? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/mesh-selector-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..62f65da52a445b81e1e816b776d722a46c7be8a2 GIT binary patch literal 720 zcmV;>0x$iEP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!a!Eu%RCwC#R!wUYQ4~FI)Feunq=bM9 zHEElwxQNNdf`lS8S(~khi2uO9AtL?&apAI!4aG%j(Jl-M){U(;g=m`(YqgEkND1U? zKD=+7NvF1Aof#~M7Y>(s?`6)Mn{(fLOjT9<?GSJP@t^eHBR#yIMUn8pp5voq$uy<Q z9*-y8YPI-KJU{!iVqUXcDO)2ok@zWTM0!SK_%x!?D25|L-zKx!B#}O#VVR)VQc7eu zDP1STMA3&(ID~jSj!UBnyjd)uSbUGn!%6-@_XrsRm)#+#mP!5ozPq7t7^&14&YeAj z533*XdVT@7Zr?$>-TryGTURRS*&3<n6XEmuu(44?E;o%vqe0Jpbh{E1Wmgps5sv6e zt&_^KjFpvT4Lth$Phn_q5QU{Bc)ebHT3bWA(^Jg!u3)xWMv;{sI7uRL5hqR_N2k+4 zFc`%7SPX8rN1J!7PZd?^A(cvk9}+(fif!=n^LfWeB}wX`R{H`~Q8n<gm&;`um&t5? zZQePhWEKO4%4hjJ0)YTrE*C1a0SJPym#M7xDOY7gv)RPUxjBfUh-+7`Xk+7<EJ~%) zj&-(`%8J~>JoRU@*}}-k1#NEO?INnxD%RIOYx9mNm8I>MecjyD@UB?gwT>~i<y?7x z=Ss#N3rv`qel#^<{+T_cvNA>XN$>bym{1S|mZ{h4<F8&k-!k=&qHlq!U8Oh3u>(}T zK2kM9<-R}rB)4jqTZggVa&&*S1!{%a&uvgYPx%KPBOKKYXb{OdiDY^<RNFpJ`ODg- zO<gj<P^&s-B$F|WvavrUvV3ze5!niM5I6kq0t^7gQ9Qm^K}9bB0000<MNUMnLSTYk CM@f4C literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/new-material_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/new-material_d.png new file mode 100644 index 0000000000000000000000000000000000000000..681da25dc1bd29885d186135ed01ac492da02e27 GIT binary patch literal 834 zcmV-I1HJr-P)<h;3K|Lk000e1NJLTq000vJ000vR1^@s6a!@wR0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!<Vi$9RCwB~l+RBSK@`WoQwmrQrf`4* zLbi}7oP@;qgQ#tdF$OQ}&0C8X5-*C0M*@VCC&jY~?b!qT0Vb_Sy->q}%CD|bgJ>xw z{k1c@-SN#9NLvU8!z6EaW@qNRpZDgyVNyz*SM#a|W9(?Gl-<Gq{KSKEY~DH|H1*le zz)+N+BZL~-xTzsAII6xIWJ$94^z?MHUW_Ssn%&F<1~vM!#3Y7^-GBq_=k=bqpKm{Y zq3h#FudN}qx(Z8(Wm-Q$qWM$ZOb4!N7O-MdzsfMeNnS9-Mn8O<Dwc}S0s%Z48^g27 zN%Zyhj?=Ql{QSKCSWM9!8WW5y337!jTqHab&^R!6^s_&|ci`Anw6?WF*L7U(=^3Wa zizmpw^bbW%2g5`&aX{mW#B-C#O28YMOLP>o`D}I%rr;2kfXM~!1%vRmddG<_)F5Uu z8L4<=s^F0`g2{avM@^43I0a8YeiAKhdAnML5CYsZ!RZa=K|t$Q+anEP703|TFuCQY zWfRm-l_BGIx6=BZ?euKPcge7A8>Y#Ljs&?$bR1n>-D-OyS%u=prJQ@Xl@34HO2=rN zuQVmEuxujYB*bf?MCiNdQ&XC^eqmWUsNbu`&CRwgS46q$x=W&5SPe;gOh#?yy*3b} zUYxM~&0>~ixdJtznqV%s=O%L#nM_iw{glIob9mg8#{PGG{nv>3YwHtT6QZ7syKe99 z?y8l^#?y3UyhI0Z;K&6bWe1VQ=m0e8!;%OxOZY9@asyurzJ)@efc^cP+8%EZd-Wp| zD?3P0ZY|~rjrt2@$UB&#xv=Q$c4MkePzjl41)1$lIMwR1s?!tYWJ;+k8V;E_ARmd; zpnRJ-oc6b0@VNmmQ)z8(d{g<BQuTRiZNC0x7#7$_g|0Qu*h-1sWyYc}Z{3*nw6q|b z{fqCJZLF`Sm5?g*@KK?&vo67>y;_vKjL?cu=am3DL-)_Tg8m6G0D_Cb+M$=kr2qf` M07*qoM6N<$f+v}hE&u=k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/new-material_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/new-material_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d13a6e4a5834f9ffedf6d79e73e16c3427055c01 GIT binary patch literal 793 zcmV+!1LpjRP)<h;3K|Lk000e1NJLTq000vJ000vR1^@s6a!@wR0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!yGcYrRCwB~lv_(wQ54759vuu}Vgf-+ zgp-g^2~9~!^x#NQJsLtcp&2XCGBqC}dYXok>Z$C8P@wY(v`?UYf{e5mvzga?Xht#T zT=qVv|2h|$GZulgV6*p}z4veZ*IsLlXg_#}d~+}aFTQ+TqMcZT{-f3UiYnEyiI=b6 zrZJ_25Xp-D0>(T>Zg^;Lz8WT~!@NE)s9~DHIss6@aU89tCh{T{i`_kc?ks)J7ie*D ziA>X+#hHFUOMeXW+Kro<QuZgM6ajKnN=aBxkKDOEwz2Vx_BJ)rsgs@5cl8prw6yf# z%G|3rlkqJWKej)FlxjvPSpcBhFbtB?0$gQ-!^L8W;S{Zn(wWmqYHr?_z~`xL${vkB zNh>K6P}8sLI)g@~B@vGC9D8$sluGOCWZ|17EqCrbah#&j+8(G&hG2lCaA*N<k+E%6 z#P789RI-|nI8w5)mJBi(X1Vt?P2(vQWYdFLGRRs<L`1)1JEK^C7=N67$RFXo3(ugL z0P$OzfLWLu8X9PM`CBpsGcA)MB9eXfblkms@BV{Sb?O;n`4FBNysNIx6&Qjki4Yt~ zmA|*al&{DKxw9W`2<E>*W_V<*3uTc*jm4{BmYWdM14$3Kwzd`&a|kx?1GrFwbjJs7 z4IM$zeLm3Erm;f<FgH}wQ}ttYmHe&<Witn_3Bxodkj*`)w`p9`Y&V;B7e?HDc*FDt zpqrbURQy@w>$wo@{kzF&q<sOkHdQX`?0tgEK-Y~i>~+`I*K=36%2<((qR!{@WZU*^ z(5InxLOor8cT~Nc^E!a*OlE?R>ySAPv0N_G%E}5mTHyZNYIOEiX_v3`Y4GMYI;0dx z2rU-dojumkk!fw+kCrOBZN-YJ8+{uru-@K_X@{H?_LGf`O?0@u{oj8<e}nDBehV-F XV(8{Wk@}~E00000NkvXXu0mjfa0Ya_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/new-material_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/new-material_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4c509a909f97a5ad012360a27562c424bf95f912 GIT binary patch literal 830 zcmV-E1Ht@>P)<h;3K|Lk000e1NJLTq000vJ000vR1^@s6a!@wR0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!;7LS5RCwB~l+RBSK@`WoQ+`<wrf`4* zLbi}7oP@;qgQ#tdF$OQ}&0C8X5-*C0M*@VCC&jY~?b!qT0Vc(xUZ~+f<yY6JL9~>T z{@R({?)YZgVp}mL#FxD6%+Aa=pZDgyVfby5oVf!jaT@dBKU!;*sGd$4+Z!B?ForOp z2Dh$ih!2ga{|5Ogd2D88X0=(2HHVq<4{7vsnMsTgw*d#**UP;x-(SD~MAs*f+}J>3 zeI1q%E3|)-M6)M`ne<=OEMU#1ahYM1le}Pvj=gz1oiF5}`Te*zK8{CIQ|RyOo1ks+ zg@py*v6xDB)tO*ySx_kCp*-Q4fX0FO+U6hM_u$xNw6%9Y*L9rl?H!@tOMj4k7#NP2 z4n~M(@`&aoiN_|96@VAC7Vpfbv+48!Ou->60h0^d3Ix#F);d9S!3Hst$#BUd6FHBZ z6-*w|JYssJ!6|tH@||dD&)VfOgb?7S2~J-y5B%DIIv#EiD?tXyM<^^GZJVTVq6itg zxs%jy>?Y?5-gAa++b~T|bR@`4qT}f9?or1Z$x4(rF6I2)on+|FPBKdKY^kYug=G^F zCm~)DB|`66uUgV-b>>=8I;cOZ=FMi?mMfw{b;Bi5F06*4JtiZYeXR`!XcQx4U$dBH zS*}1us3Mrj9Mp=ro=mP%u6<PXhEuz4n!oBFxJ1k!+aBne5cOo-4SRooAJwj?yG=*N z3UmQSj$9N{b`oinE<mF(EQtWKgwL`qH}RF^Tgc^dI6Ta#<FN*@XJ1m$qJsq0)>4Mh zXuL>{tb=J<3yH2CH>c_bm5^zcklNjbQ!cO6`m~{)Oeu9m!y)5G6eE!uRByA@Wnae` zubc29wbu65Csl6=)t@K!=JRicVS$a7sQ)=*YX$li8H+r<c4f}f(t>pQCqAckvALO4 zLaNb2^^MN{_7Fbo(;^gQm{x;b7yRf7-v0L!blO+-B>N@601uG9)V&w|9RL6T07*qo IM6N<$g4ITf6aWAK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/pyramidpreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/pyramidpreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..285f42ae21fa483a313fdf9d1569a54d9946b088 GIT binary patch literal 1197 zcmb_byGjE=6rE)AerytL1pgpnW0TGeNWez0uum3}B48j$5Cy^ffnAz_h+iS3SWpqf z!b%Za3u}8Jo-;dl9d#`PZ*um&&&=%JSyD%8Iib{>QmP1zzXnvKX~zj`MWoL`ASprU zvy;{4&c55ULh9k*Hh2WiTQ>o054H!}6OK8A_0$yd5%zjvW<KY7DIg8ll7S<9jAw!I z)9FmL?`M;{>mUa|4-^1oJgx930wtg<flu8OlYOpnD$ueH_w?Ml3<##7J)%C{Xws+D zr&k`&Qqx}eVBXY9U&JA0R*l}PUH`3yy>^YZV`}WRq&`|Vj-MPRZr5wuoJZT?`;>R8 z^?$|9F@#%@7oab(P`_{Xg4c_ebvDG=i0M7C<~Q5)dKS+Qo7}83Jh3+Op^SC@FSeQM z4tl;|F(irRs0yFoU_r+dxG#=M;Z|hy<TE%w&SByUnD7#&+f|(0EPf7R4kC`K0+Ms# zmEa49^m5vh$kn(~BI%^knQSg!D3;1@WipvG8V%QVJDtu7rCRIuMt*;HO>H0U`GbBp hGR!_f!OK$__@h?0J@ALaUU$nMZE(L3x#aSJKLAS=@C*O| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/screenFaded.png b/Templates/BaseGame/game/tools/materialEditor/gui/screenFaded.png new file mode 100644 index 0000000000000000000000000000000000000000..a5125a3ab8452cfae852210dc10e7ff7a588e13b GIT binary patch literal 4926 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/scrollBox.jpg b/Templates/BaseGame/game/tools/materialEditor/gui/scrollBox.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd15e5fcd2c7025296e2b84268295cdaff9fa068 GIT binary patch literal 12998 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 E0kmZyVgLXD literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/spherepreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/spherepreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..e7b208f61059573b7bf5e02d0d0a426ffaa5283d GIT binary patch literal 22077 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/torusknotpreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/torusknotpreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..55208ac35e23dd490aa1af8f45b642b50bc3766e GIT binary patch literal 40585 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/torusknowpreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/torusknowpreview.dts new file mode 100644 index 000000000..e69de29bb diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/toruspreview.dts b/Templates/BaseGame/game/tools/materialEditor/gui/toruspreview.dts new file mode 100644 index 0000000000000000000000000000000000000000..8c315339684107a4814df390ea3267ec6f179333 GIT binary patch literal 12705 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 Ca4Z=B literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/unknownImage.png b/Templates/BaseGame/game/tools/materialEditor/gui/unknownImage.png new file mode 100644 index 0000000000000000000000000000000000000000..21d75f3115cd097dc9aaa538c9cd30a116de01fa GIT binary patch literal 689 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911L)MWvCLm(wt;u=vBoS#-wo>-L1;Fyx1 zl&avFo0y&&l$w}QS$HzlhJk@8*we)^q~g|_I|q4>C<w4P7AvWL`u}jnG0Qnyv{hCy zZFqjUNj&-e-;k*6sdfKr<A1;XfBSu7HzxyQ1A_yD0)qep2LlTO6S3S9DNnEZ2Y;)7 z``>%dazK=W1g%K+Tu7<1zxQwUcWcH1RwnWsVj?-}5Y&jHQiwbePGAVzlpW7zx2heO P1{pkE{an^LB{Ts5&{T$6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/unsavedWarn.png b/Templates/BaseGame/game/tools/materialEditor/gui/unsavedWarn.png new file mode 100644 index 0000000000000000000000000000000000000000..cf7ecda6b0fba9a60c729c22b03bdbc16221f3ca GIT binary patch literal 2990 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_d.png new file mode 100644 index 0000000000000000000000000000000000000000..d7629c6699021bd1f6c8a7063d4d53f85d126fe9 GIT binary patch literal 384 zcmV-`0e}99P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwB)QlSpQKn#6c5St;0B{LIb zk{`g%z%&VhAf^^H0*%5kf#wTXLV}w>@fBzUY7)5_?%lE7!bnJH()6zFYhQcUiQ~8r z;4C%3Z@EgHb_uX7D+t37j^jWG@in_FOJrFV<avH(h64D$57)Ng?sjm;W4NP{w!BNf z=Xr>ti1<L2Wd~bU5{m`!d;-TKaKCGNy8-jL@=Hdls{)ad*lr=q0fz%H9BNJkP$~O; zP1Ou=xd5w`8RB#T)@vY1wDSIzZha!HszVbno0)hE)aw~Kp8)z8Z-FKgoy>$LpqhvA z)=Zm?wxrlx#BvEd9wuIa_;9K{S4;p`JQ!%pjnXgUMN!oBu6$`eoJAn1eGvlEG*w@z e^UvRV2`~T$pJ{d;FOIzc0000<MNUMnLSTY-_MO%M literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bf14a5ecd78ffb5f5ba1cca269cc9e2146331677 GIT binary patch literal 458 zcmV;*0X6=KP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzY)M2xRCwB)lD|trQ546&nvqXMO0Y0e zpU~1DVBjLpMz^=s;ASqOHcC!8=_Wemtl;)0T~ctf|DZ{qPl%9yEk0kw>)E|ml;}Z1 z2M+hg_k7N|hkGo^WO5gPL|njT>k}7k6o|*;f+&g`2}zQKPNySLhyj|WL7&Yqsn_u~ z7~o4)vE_8a6Nw;@O2OuGfyNO?u&%OD;31!fPJ&NLC7ia~Y*k02<vr0?wuY4YEp`s} zaa<^X3x%*)ET9gDm=lQFW3y?7-Us*aonNqE>d)Of$dwBCs~q@Tj-0z|$hO+h9_&01 z!!YpL>p`hj5zJ=ciJh%=6GWS)&(UNVlUfadOa^<g7)wI$sBVJX;V{eoF&^V6orXIa zg|6#Ndup|qZtBUKNPsj0doYM?zn=lLvQjSNb2?q>zbjcd4u^$nyfhk6ibWXnIjjx` ztLRN#GW{pir$4=%2p<AB#lI$kjgsX*v-d5)0FpPKpqJul3IG5A07*qoM6N<$f?q$( AYXATM literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_i.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_i.png new file mode 100644 index 0000000000000000000000000000000000000000..7fc641c933f4ce70ec73d64d4702c7850614e286 GIT binary patch literal 416 zcmV;R0bl-!P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzLP<nHRCwBAw6d~dW?*1Y24Y?Y!r(Iy z-(>-5@bdEF&Ckzg;NjuHsrt*8FANhWPUPLTZJRP9&?e)ZJ9mOLynFYK4am0q@Zp2P zzkmPOnV6V<3J3^1aCdh<E+i!M3#1XqW?%xD0@JX5{d(U&fBvxB+S+c(&d#0%qQ8Cn zCIS)%$-`X21hXDw0~;IL=h)cTT{=2CZ$V<upFbD=@#BXuh`o33USpVAxF(R33=Iv> z!uY#)?^e5Y>z0F>n%V(#bMpgl-n>!6&;;@W%m!o)uCA`PK;j^Im|7ONZlI^HT)83) zauTuzkQ_)HB#)s96iO#go^$~D0a*hGoIiiw7$lG3JW#NLXbujJw~~^QZ(%kpUcA`t z*RNl^FnJIl7*8T1BDb8KozEi26G)R92`PdBm?Azil9ToT0t^5>g|$%3I*2j=0000< KMNUMnLSTX#wz0eb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-none_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e28974ad7d2ecad43a0600177c6e9d7d13521908 GIT binary patch literal 479 zcmV<50U-W~P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzfk{L`RCwB)lD})hP!PwTHAB$BF6wg& zh>%fH2SIU&Aczv2>Xs#cL-zhDf<rpVLl6Rj)=AJnMo|Yr#JDLa2K?1KA*Cf0p@HK) z?t7nmce(Sas>%~W8ejr>{w=|D4uMLgBGl{kr$oEm7P{ST1EB|SI-SUVzvnxhPI<fC zikHiU=Qz%ZMx)bOt!9KmAqV3C5?<Ua2((%)<$ON#)oQh=Y1%uU;HT(E-HrW*@P$Ai zuu~M}Baukh_xqi!*K2w>94P9BVH7<OmXyooJ~0?=Fc>7q<FTC0W|<^OY_(b?-B`}O zU=Q(l+`ipzWH=lqCzFYs$z)ieP_SIx17Xv%*^F*B8)}-SoKB}%KA*QR$zrjfsC%Gr zI6Uq5`||O4q^VSjL8%sGe`ur8sEE3!C#c)NGZ+lokx0ZwU_PHyUDw}W9Ivka>m>+w zU>;5Q0p(t=r(CaB-tYH2u~^J1l}ZftpU@H~je~;#M-)W{KK92(JXXo~pWXWsU;ww` VzVNSWJr)1}002ovPDHLkV1l*Y*uVe) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_d.png new file mode 100644 index 0000000000000000000000000000000000000000..eb8e9c684ea13140ab791c328d7403f49efd08a5 GIT binary patch literal 360 zcmV-u0hj)XP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz3Q0skRCwB)lCg@yKoEvM!xc#*SELcF zQu+iI7O6~qAYzq5g0;_Joyyt=Fd*2au(GtVvG55*A*EGFn#=6q9TL63{jke2JKyd< zv)hWI=o8ph!>3vMYJ7bNVB5AI1Ocw=Vp-P9?6NFL)6~zhY%7ofJkP^%oVSoD3`62L z_60IG)PVaP77I8W;BtXp4|_B+TLzNk8ZepYdcB5jSK-+V#$$zQ+U?+chSLe!ZH@K& zFrUNqiXMNvJwswgk{-ZA5))@;J|3Sp<(5~f>iUINE3HGc*{Hqg^lcZlrcMV21GOV? z9K&$<H&iZ{+Mxy&Me$>AU%fPWo;LzXl9;d5`p@6{7GMB14p723HDBTY0000<MNUMn GLSTXm0-A*Y literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_h.png new file mode 100644 index 0000000000000000000000000000000000000000..6570fe142940bd68cbc41bd3827d1fac5fb59383 GIT binary patch literal 397 zcmV;80doF{P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzFG)l}RCwBAOi4)*U|?Wa55x`(guwwI z4rBpo@bdC<2n-A)sAK*5^$y#%ZCel01lI8V`*()_|NrCEz{tqR08$6k<iG+GMAz{5 z>sN+Hvt}`Tx_p`8=gXH2TuMp|Ql6e*IjovM8g5ipg4ybAZ4B(<;tZdGn(wr>GB8L; zFfbu&g5h_^k1>3^cMq(AO-u|d#-pRdpb#GqRtwhj<HZYxfB*i$@y{188F+z;7&$pn z#03lt5Sl(*xPT)0?f!iRkYka>K$u5G1)=G|!i5Y!KYnER^XCu4n>~9NKAt?uAO!R@ zh>wh602~k;K+oOE%3@&R=4SW@3<SxH3<fSEBNT<m0IbQNzn|gx^XCkI|Nezbple`e rW=7F;02I?irU-CSVk9T+0R$KTfXuJbaPB(j00000NkvXXu0mjftw5<! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_i.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_i.png new file mode 100644 index 0000000000000000000000000000000000000000..0008c320e3f11cb6f1b667c46bd39104ede284c7 GIT binary patch literal 373 zcmV-*0gC>KP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7fD1xRCwBAw6d~dW?*1Y24Y?Y!r(Iy z-(>-5@bdEF&Ckzg;NjuHsrt*8FANhWPUPLTZJRO^$Ob|hKma5UQU_Ac0uw+7@7}#* z+qrY6<>$|zrGEeZ&C9{T@m4}Y;ucWqG7C0SK^oStU+)WIySln=QB+j?eCpIG$+KtA zy5ZCWlvMcf<A-o$WaI)NA)#L&F*7r>=dWMCJ_llh{KRno{(W9FPxEqfb3cP=fB|`V z`DbuVckbMgMAIaU%>_C-I&YC}G6wkr#NNGox7w#qpA?jol&;_kNwA;T*w{X=T)8rm znVI?5-@kv^)YQ}t*xA`Vz^f@FBxDPrc!EVR%z2-IF%1_WkRrfIiIJSN2M}NY$^vn( TtUbAI00000NkvXXu0mjf{DP*8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-sine_n.png new file mode 100644 index 0000000000000000000000000000000000000000..9595622a6d0b23be29dd3f40555a0c25fa772568 GIT binary patch literal 406 zcmV;H0crk;P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzI7vi7RCwB)lCf)pKp4f(>L8LKPHi&X z5{ZlYA9O6Zg$xZYU0kJqQy@Y&DFH{ptz8r+I|>;R2tgx)4n}jIV7P{sls<THzW4p$ z@IJ;gO&%e%Ajasn65=-w0A1JdY&N?|IF5r|*IfYA0cBaDwrzg}7=}SGM<C4g;Wd!w zIlo@7pALruuBvJ{7z}ojBt@k9_z?oYw(S=<&*$^ycs$OcD8ioSJp*({L%o9_m=;Cx zU>L@7G#X`)DT<O1`!L%<o6Tm}G!5!~JRXOlC?@1e6`fBeljH)$ag6CQP1AeG=?pB( z^6mnKVfaV}`MR!=@B7+*zn>C)!Z+<+-H#9iAzQ6huN=o!q~tYCdskKUiyoxaL6&7L zx7#f`olckH8em!W8<fCH|8XO_Rbu|LduIU#0M0qU{%a<XZU6uP07*qoM6N<$f+zf_ A*8l(j literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_d.png new file mode 100644 index 0000000000000000000000000000000000000000..f51cbf30190cd98cb94e74fbacad121da1e223fa GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$ZGeiFc)B=-RNPAP@$?i-NJxm_Kf=e;a*$DhFNHDl!Gi|{1?A=G z0)m2x2}wzZR{i?^{yxv`ZMnr2Ute9VnxM9UO;uHu&Ckzo&MXH7J-xn#%a<Q#T*8^d z+{VVr%Hu1eprqQ$xQ26y#A;rCxrDB*TMP^gCor<@R4Z^Wm@#|ux|iQ4D)aCd8L^1| z|Mivo-5%xxGtz#1e9Z31s~x_MMP1dc=#rd610%!nIoc2YHSVnfx|hMz)z4*}Q$iB} DC=Oc- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_h.png new file mode 100644 index 0000000000000000000000000000000000000000..0643cd0767c34ccf977492d5a0e937a306eda1c8 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$ZGeiFdb&7<RNPu}$(rx50uMug@}`Yn1a>-=Z<^F;Jz;U_H=&Jf zJH;Pz=+tmcl+xr?3C%vqZ05fyMR@5IUIqj8_SXiMso@O*P2QUNtV%^|O~tk_J(pQB zWmba#)6;mjJ=Nm&2a9E1?|Q$m)9cRj%I|4C2a{)=3(UU5dsKq|Y}DKOCwuKpvbXZi zOk82+FBmVj;hGc2!cL8))+wi!?cdW;YVhgw+r2Cj4DbF0eC1I#*$Q+pgQu&X%Q~lo FCICq}VoLx3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_i.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_i.png new file mode 100644 index 0000000000000000000000000000000000000000..1f6d750f59fb0e8573d1e1f2074a91f4544b2525 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$ZGei}JzX3_DsClh*|aGsEg>O*Eh&!e&;w(E`>BkXA3lA0boTuD z^al?g9$fkB^mKh5VRgSJcemxvHWEI;<|O67pu!)-9(0~b*nvHGxu5Uhh4o_Gc9%DA z-kiR$TQ_=JPh)2zkHHDkjRG8+cV<0${5V~(OFe?+@SN|G5|;}YnKYT^Op!S0S<S%6 a#*nO_RXFS94n?4o7(8A5T-G@yGywo3gHwqB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-square_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3a99a5aac469480f71adec46ce387e95c722fb2c GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2@wC9V-A!TD(=<%vb942~)JNvR5+ zxryniL8*x;m4zo$ZGeiFdb&7<RNPAP@$`I{kdV;9ki^P%=mDd^`UJ+z51&2-aq#e{ ztVsU*>nqpoZMoeHnM~O~{{6K+{Qv*|!mNaploW=Q%vnJRjhu_JU%Yy?>iEL_g3Hwe zG<0=yU7i&e7aK2Gw#?|k)t1vc3w;0o`+Io85jD4+%k1m_C3qwWH)m&MWE_wRYB4dj ztN%CW;rsjhEldrKjTdKBF?3}r1+!Ra8cv%0je&`WVa~k33(leUjsYFa;OXk;vd$@? F2>_RaX9EBL literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_d.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_d.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca098768245019b43b11e744b67271680ac2570 GIT binary patch literal 352 zcmV-m0iXVfP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz0!c(cRCwB)lCchgFc5}+h%qoo97tT8 z7<mdNncbKe3~_ewF*-Q0kTApK#%y>9o`8wbiNIp7Cy0?~;-55a)BEoJYpX?3v<7gM z!^3ag<k&9=Fbu=<eIK@ML)Z1$=)Uh!6ouC`&6OYpIF19;G~c1R2DaOA8-^i-5T1&l z3LgOJcm&SpaVs6ESw&Is2XMLYTvb320QdWNG~WQ(B1i}z%NUq|3?<1sM3zn`z;yx3 zg1iA<6WDc6s4QRJ)08t3G!<3d?IxAtn7vcugRMDHqUG`ltyb)vdN~}}$`>t$hr?#W y-oL1m{S5YQ@}<$X?N1=jbM=+9fBxP>fB^sue^*Si!X{h*0000<MNUMnLSTYDc$GQ; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_h.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_h.png new file mode 100644 index 0000000000000000000000000000000000000000..42549866faa18526a06cb3ba5265a5148d7eea69 GIT binary patch literal 398 zcmV;90df9`P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzFiAu~RCwBAOi4)*U|?Wa55x`(guwwI z4rBpo@bdC<2n-A)sAK*5^$y#%ZClR-vH_&w`}gk*-@biA!SC<ihqFQQP=g(qV5a>4 z{~yET+bt~&_xt-X<WV#s<0ng(fW^PwyT@?7qJrVimoNA={doC;;n}j~3<~-A4C-xd z41d3VWw=pU3D*eL^kBgPhJXM5!SRFs2@H}E5eys(3Ji>#oD3R3Cvqw(FkC1sWMBiz zgEhT5dX(Y8#ED=HFE?&v_yaUqJUkr5AlaN;1`%s(giQuOlfD7ney_cq;l;|84ANOy z41fOo!N3X$2@JoOm>`;1cz77(YHJyOy?n_a=;6V@A|r!U6AZAx7$CRH0D~9=kQKlH sGcz-arURguCNf2UlM*92X%8U403pW3QZy6bVE_OC07*qoM6N<$f^iC^r2qf` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_i.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_i.png new file mode 100644 index 0000000000000000000000000000000000000000..b63963b77cf4bd15e3c13d28f9d593d720c927cb GIT binary patch literal 372 zcmV-)0gL{LP)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz7D+@wRCwBAw6d~dW?*1Y24Y?Y!r(Iy z-(>-5@bdEF&Ckzg;NjuHsrt*8FANhWPUPLTZJRO^$Oc>*?%%%;w;d!8QU_Ac1QWmp z_U+r}wtV?=dt{9uz=X>=>({S02GQTXeG^%*V1duOckkF>a#%Hiob~M4Gh=ge^8<l_ zft&yS{mTwC#22QK3E5N_IC$`&y_A&HSsfjnw?aZfzmk)a*K%=jy#Z-tXJ=;vYXX{} z0CW4+ty^_LCIf9ai)>J2WaL4h0T19dft&zx`{Kom-5x!9WCU^+HfMtj`uX$cFR&(% z6F?e&{rbf#At7-E<ScwK%mRxznA`Cw2C0KN?=vu_;Q|Cw1UM-%l9ToT0t^5=1AZUt S`eP;l0000<MNUMnLSTY|3Y|~@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_n.png b/Templates/BaseGame/game/tools/materialEditor/gui/wav-triangle_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6f05c017559142960276a08bba0b1b2bb159e750 GIT binary patch literal 415 zcmV;Q0bu@#P)<h;3K|Lk000e1NJLTq000dD000dL1^@s6a_i)L0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzK}keGRCwBAOi4-MU|?W~0Ad9O!r%cA zuV4Xb@bdCf2n-A)sAK*5^$OdzZHr(6*#Oe;{rh)@Z{NP5;M=!v3&7YQd8ok(OfXaa z|NoC+^2UuD!<Q~y>W?muq6rxTH8_C8KYsiuF?;sxcp%MzSJTs{PX%t?ylL<3?7T55 zDr({H-@iG4Ho`SB8yOin{QUXTL0MV(0Zh~C)vLqh<m4_|SXf+PV`KYcY;1h?&Ye3l z$B!SkdH??XRj^Hu9z9ZDv0{b)zkmN2cJ10_@$1(w4q%8L0P$fsG&FQ0(4d=e=K<qt zA;|4ZmMjUse*L;V5N!PO=MMuq25LO;_wU~yU`_n|{6B!fxeyqv0tyNWCxIruz@`ZX zSYQlpZtfqEk&!b&Y-9y6z|72yqUixBrin}u;H1PzPTB(qFaU@*-IbJg;iUio002ov JPDHLkV1fv3xmy4L literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/materialEditor/main.cs b/Templates/BaseGame/game/tools/materialEditor/main.cs new file mode 100644 index 000000000..4751676fe --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/main.cs @@ -0,0 +1,160 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Material Editor Written by Dave Calabrese and Travis Vroman of Gaslight Studios + +function initializeMaterialEditor() +{ + echo(" % - Initializing Material Editor"); + + // Load Preview Window + exec("~/materialEditor/gui/guiMaterialPreviewWindow.ed.gui"); + + // Load Properties Window + exec("~/materialEditor/gui/guiMaterialPropertiesWindow.ed.gui"); + + // Load Client Scripts. + exec("./scripts/materialEditor.ed.cs"); + exec("./scripts/materialEditorUndo.ed.cs"); + //exec("./gui/profiles.ed.cs"); + + MaterialEditorPreviewWindow.setVisible( false ); + matEd_cubemapEditor.setVisible( false ); + matEd_addCubemapWindow.setVisible( false ); + MaterialEditorPropertiesWindow.setVisible( false ); + + EditorGui.add( MaterialEditorPreviewWindow ); + EditorGui.add( matEd_cubemapEditor ); + EditorGui.add( matEd_addCubemapWindow ); + EditorGui.add( MaterialEditorPropertiesWindow ); +} + +function destroyMaterialEditor() +{ +} + +// Material Editor +function MaterialEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Material Editor", "", MaterialEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Material Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "MaterialEditorPlugin", "MaterialEditorPalette", expandFilename("tools/worldEditor/images/toolbar/matterial-editor"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( MaterialEditorPropertiesWindow, MaterialEditorPreviewWindow); + + %map = new ActionMap(); + %map.bindCmd( keyboard, "1", "EWorldEditorNoneModeBtn.performClick();", "" ); // Select + %map.bindCmd( keyboard, "2", "EWorldEditorMoveModeBtn.performClick();", "" ); // Move + %map.bindCmd( keyboard, "3", "EWorldEditorRotateModeBtn.performClick();", "" ); // Rotate + %map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale + %map.bindCmd( keyboard, "f", "FitToSelectionBtn.performClick();", "" );// Fit Camera to Selection + %map.bindCmd( keyboard, "z", "EditorGuiStatusBar.setCamera(\"Standard Camera\");", "" );// Free Camera + %map.bindCmd( keyboard, "n", "ToggleNodeBar->renderHandleBtn.performClick();", "" );// Render Node + %map.bindCmd( keyboard, "shift n", "ToggleNodeBar->renderTextBtn.performClick();", "" );// Render Node Text + %map.bindCmd( keyboard, "alt s", "MaterialEditorGui.save();", "" );// Save Material + //%map.bindCmd( keyboard, "delete", "ToggleNodeBar->renderTextBtn.performClick();", "" );// delete Material + %map.bindCmd( keyboard, "g", "ESnapOptions-->GridSnapButton.performClick();" ); // Grid Snappping + %map.bindCmd( keyboard, "t", "SnapToBar->objectSnapDownBtn.performClick();", "" );// Terrain Snapping + %map.bindCmd( keyboard, "b", "SnapToBar-->objectSnapBtn.performClick();" ); // Soft Snappping + %map.bindCmd( keyboard, "v", "EWorldEditorToolbar->boundingBoxColBtn.performClick();", "" );// Bounds Selection + %map.bindCmd( keyboard, "o", "objectCenterDropdown->objectBoxBtn.performClick(); objectCenterDropdown.toggle();", "" );// Object Center + %map.bindCmd( keyboard, "p", "objectCenterDropdown->objectBoundsBtn.performClick(); objectCenterDropdown.toggle();", "" );// Bounds Center + %map.bindCmd( keyboard, "k", "objectTransformDropdown->objectTransformBtn.performClick(); objectTransformDropdown.toggle();", "" );// Object Transform + %map.bindCmd( keyboard, "l", "objectTransformDropdown->worldTransformBtn.performClick(); objectTransformDropdown.toggle();", "" );// World Transform + + MaterialEditorPlugin.map = %map; + + MaterialEditorGui.fileSpec = "Torque Material Files (materials.cs)|materials.cs|All Files (*.*)|*.*|"; + MaterialEditorGui.textureFormats = "Image Files (*.png, *.jpg, *.dds, *.bmp, *.gif, *.jng. *.tga)|*.png;*.jpg;*.dds;*.bmp;*.gif;*.jng;*.tga|All Files (*.*)|*.*|"; + MaterialEditorGui.modelFormats = "DTS Files (*.dts)|*.dts"; + MaterialEditorGui.lastTexturePath = ""; + MaterialEditorGui.lastTextureFile = ""; + MaterialEditorGui.lastModelPath = ""; + MaterialEditorGui.lastModelFile = ""; + MaterialEditorGui.currentMaterial = ""; + MaterialEditorGui.lastMaterial = ""; + MaterialEditorGui.currentCubemap = ""; + MaterialEditorGui.currentObject = ""; + + MaterialEditorGui.livePreview = "1"; + MaterialEditorGui.currentLayer = "0"; + MaterialEditorGui.currentMode = "Material"; + MaterialEditorGui.currentMeshMode = "EditorShape"; + + new ArrayObject(UnlistedCubemaps); + UnlistedCubemaps.add( "unlistedCubemaps", matEdCubeMapPreviewMat ); + UnlistedCubemaps.add( "unlistedCubemaps", WarnMatCubeMap ); + + //MaterialEditor persistence manager + new PersistenceManager(matEd_PersistMan); +} + +function MaterialEditorPlugin::onActivated( %this ) +{ + if($gfx::wireframe){ + $wasInWireFrameMode = true; + $gfx::wireframe = false; + }else{ + $wasInWireFrameMode = false; + } + advancedTextureMapsRollout.Expanded = false; + materialAnimationPropertiesRollout.Expanded = false; + materialAdvancedPropertiesRollout.Expanded = false; + WorldEditorPlugin.onActivated(); + + EditorGui-->MatEdPropertiesWindow.setVisible( true ); + EditorGui-->MatEdPreviewWindow.setVisible( true ); + EditorGui-->WorldEditorToolbar.setVisible( true ); + + MaterialEditorGui.currentObject = $Tools::materialEditorList; + // Execute the back end scripts that actually do the work. + MaterialEditorGui.open(); + %this.map.push(); + + Parent::onActivated(%this); +} + +function MaterialEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + WorldEditorPlugin.onEditMenuSelect( %editMenu ); +} + +function MaterialEditorPlugin::onDeactivated( %this ) +{ + if($wasInWireFrameMode) + $gfx::wireframe = true; + + WorldEditorPlugin.onDeactivated(); + + MaterialEditorGui.quit(); + + EditorGui-->MatEdPropertiesWindow.setVisible( false ); + EditorGui-->MatEdPreviewWindow.setVisible( false ); + EditorGui-->WorldEditorToolbar.setVisible( false ); + %this.map.pop(); + + Parent::onDeactivated(%this); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.cs b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.cs new file mode 100644 index 000000000..b4e85229f --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditor.ed.cs @@ -0,0 +1,2270 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Material Editor originally created by Dave Calabrese and Travis Vroman of Gaslight Studios + +function MaterialEditorGui::establishMaterials(%this) +{ + //Cubemap used to preview other cubemaps in the editor. + singleton CubemapData( matEdCubeMapPreviewMat ) + { + cubeFace[0] = "tools/materialEditor/gui/cube_xNeg"; + cubeFace[1] = "tools/materialEditor/gui/cube_xPos"; + cubeFace[2] = "tools/materialEditor/gui/cube_ZNeg"; + cubeFace[3] = "tools/materialEditor/gui/cube_ZPos"; + cubeFace[4] = "tools/materialEditor/gui/cube_YNeg"; + cubeFace[5] = "tools/materialEditor/gui/cube_YPos"; + parentGroup = "RootGroup"; + }; + + //Material used to preview other materials in the editor. + singleton Material(materialEd_previewMaterial) + { + mapTo = "matEd_mappedMat"; + diffuseMap[0] = "tools/materialEditor/gui/matEd_mappedMat"; + }; + + singleton CustomMaterial( materialEd_justAlphaMaterial ) + { + mapTo = "matEd_mappedMatB"; + texture[0] = materialEd_previewMaterial.diffuseMap[0]; + }; + + //Custom shader to allow the display of just the alpha channel. + singleton ShaderData( materialEd_justAlphaShader ) + { + DXVertexShaderFile = "shaders/alphaOnlyV.hlsl"; + DXPixelShaderFile = "shaders/alphaOnlyP.hlsl"; + pixVersion = 1.0; + }; +} + +function MaterialEditorGui::open(%this) +{ + MaterialEditorGui.establishMaterials(); + + // We hide these specific windows here due too there non-modal nature. + // These guis are also pushed onto Canvas, which means they shouldn't be parented + // by editorgui + materialSelector.setVisible(0); + matEdSaveDialog.setVisible(0); + + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(0); + + //Setup our dropdown menu contents. + //Blending Modes + MaterialEditorPropertiesWindow-->blendingTypePopUp.clear(); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(None,0); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(Mul,1); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(Add,2); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(AddAlpha,3); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(Sub,4); + MaterialEditorPropertiesWindow-->blendingTypePopUp.add(LerpAlpha,5); + MaterialEditorPropertiesWindow-->blendingTypePopUp.setSelected( 0, false ); + + //Reflection Types + MaterialEditorPropertiesWindow-->reflectionTypePopUp.clear(); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.add("None",0); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.add("cubemap",1); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.setSelected( 0, false ); + + //Sounds + MaterialEditorPropertiesWindow-->footstepSoundPopup.clear(); + MaterialEditorPropertiesWindow-->impactSoundPopup.clear(); + + %sounds = "<None>" TAB "<Soft>" TAB "<Hard>" TAB "<Metal>" TAB "<Snow>"; // Default sounds + + // Get custom sound datablocks + foreach (%db in DataBlockSet) + { + if (%db.isMemberOfClass("SFXTrack")) + %sounds = %sounds TAB %db.getName(); + } + + %count = getFieldCount(%sounds); + for (%i = 0; %i < %count; %i++) + { + %name = getField(%sounds, %i); + MaterialEditorPropertiesWindow-->footstepSoundPopup.add(%name); + MaterialEditorPropertiesWindow-->impactSoundPopup.add(%name); + } + + //Preview Models + matEd_quickPreview_Popup.clear(); + matEd_quickPreview_Popup.add("Cube",0); + matEd_quickPreview_Popup.add("Sphere",1); + matEd_quickPreview_Popup.add("Pyramid",2); + matEd_quickPreview_Popup.add("Cylinder",3); + matEd_quickPreview_Popup.add("Torus",4); + matEd_quickPreview_Popup.add("Knot",5); + matEd_quickPreview_Popup.setSelected( 0, false ); + matEd_quickPreview_Popup.selected = matEd_quickPreview_Popup.getText(); + + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.clear(); + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.add("Layer 0",0); + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.add("Layer 1",1); + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.add("Layer 2",2); + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.add("Layer 3",3); + MaterialEditorPropertiesWindow-->MaterialLayerCtrl.setSelected( 0, false ); + + //Sift through the RootGroup and find all loaded material items. + MaterialEditorGui.updateAllFields(); + MaterialEditorGui.updatePreviewObject(); + + // If no selected object; go to material mode. And edit the last selected material + MaterialEditorGui.setMode(); + + MaterialEditorGui.preventUndo = true; + + if( MaterialEditorGui.currentMode $= "Mesh" ) + MaterialEditorGui.prepareActiveObject( true ); + else + MaterialEditorGui.prepareActiveMaterial( "", true ); + + MaterialEditorGui.preventUndo = false; + +} + +function MaterialEditorGui::quit(%this) +{ + // if we quit, restore with notDirty + if(MaterialEditorGui.materialDirty) + { + //keep on doing this + MaterialEditorGui.copyMaterials( notDirtyMaterial, materialEd_previewMaterial ); + MaterialEditorGui.copyMaterials( notDirtyMaterial, MaterialEditorGui.currentMaterial ); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + + if( isObject(MaterialEditorGui.currentMaterial) ) + { + MaterialEditorGui.lastMaterial = MaterialEditorGui.currentMaterial.getName(); + } + + MaterialEditorGui.setMaterialNotDirty(); + + // First delete the model so that it releases + // material instances that use the preview materials. + matEd_previewObjectView.deleteModel(); + + // Now we can delete the preview materials and shaders + // knowing that there are no matinstances using them. + matEdCubeMapPreviewMat.delete(); + materialEd_previewMaterial.delete(); + materialEd_justAlphaMaterial.delete(); + materialEd_justAlphaShader.delete(); +} + +function MaterialEditorGui::openFile( %this, %fileType ) +{ + switch$(%fileType) + { + case "Texture": %filters = MaterialEditorGui.textureFormats; + + if(MaterialEditorGui.lastTextureFile $= "") + %defaultFileName = "*.*"; + else + %defaultFileName = MaterialEditorGui.lastTextureFile; + + %defaultPath = MaterialEditorGui.lastTexturePath; + + case "Model": %filters = MaterialEditorGui.modelFormats; + %defaultFileName = "*.dts"; + %defaultPath = MaterialEditorGui.lastModelPath; + } + + %dlg = new OpenFileDialog() + { + Filters = %filters; + DefaultPath = %defaultPath; + DefaultFile = %defaultFileName; + ChangePath = false; + MustExist = true; + }; + + %ret = %dlg.Execute(); + if(%ret) + { + switch$(%fileType) + { + case "Texture": + MaterialEditorGui.lastTexturePath = filePath( %dlg.FileName ); + MaterialEditorGui.lastTextureFile = %filename = %dlg.FileName; + + case "Model": + MaterialEditorGui.lastModelPath = filePath( %dlg.FileName ); + MaterialEditorGui.lastModelFile = %filename = %dlg.FileName; + } + } + + %dlg.delete(); + + if(!%ret) + return; + else + return makeRelativePath( %filename, getMainDotCsDir() ); +} + +//============================================================================== +// SubMaterial(Material Target) -- Supports different ways to grab the +// material from the dropdown list. We're here either because- +// 1. We have switched over from another editor with an object locked in the +// $Tools::materialEditorList variable +// 2. We have selected an object using the Object Editor via the Material Editor + +function SubMaterialSelector::onSelect( %this ) +{ + %material = ""; + + if( MaterialEditorGui.currentMeshMode $= "Model" ) + %material = getMapEntry( %this.getText() ); + else + %material = MaterialEditorGui.currentObject.getFieldValue( %this.getText() ); + + %origMat = %material; + if(%material$="") + %origMat = %material = %this.getText(); + // if there is no material attached to that objects material field or the + // object does not have a valid method to grab a material + if( !isObject( %material ) ) + { + // look for a newMaterial name to grab + // addiitonally, convert "." to "_" in case we have something like: "base.texname" as a material name + // at the end we will have generated material name: "base_texname_mat" + %material = getUniqueName( strreplace(%material, ".", "_") @ "_mat" ); + + new Material(%material) + { + diffuseMap[0] = %origMat; + mapTo = %origMat; + parentGroup = RootGroup; + }; + + eval( "MaterialEditorGui.currentObject." @ strreplace(%this.getText(),".","_") @ " = " @ %material @ ";"); + + if( MaterialEditorGui.currentObject.isMethod("postApply") ) + MaterialEditorGui.currentObject.postApply(); + } + + MaterialEditorGui.prepareActiveMaterial( %material.getId() ); +} + +//============================================================================== +// Select object logic (deciding material/target mode) + +function MaterialEditorGui::setMode( %this ) +{ + MatEdMaterialMode.setVisible(0); + MatEdTargetMode.setVisible(0); + + if( isObject(MaterialEditorGui.currentObject) ) + { + MaterialEditorGui.currentMode = "Mesh"; + MatEdTargetMode.setVisible(1); + } + else + { + MaterialEditorGui.currentMode = "Material"; + MatEdMaterialMode.setVisible(1); + EWorldEditor.clearSelection(); + } +} + +function MaterialEditorGui::prepareActiveObject( %this, %override ) +{ + %obj = $Tools::materialEditorList; + if( MaterialEditorGui.currentObject == %obj && !%override) + return; + + // TSStatics and ShapeBase objects should have getModelFile methods + if( %obj.isMethod( "getModelFile" ) ) + { + MaterialEditorGui.currentObject = %obj; + + SubMaterialSelector.clear(); + MaterialEditorGui.currentMeshMode = "Model"; + + MaterialEditorGui.setMode(); + + for(%j = 0; %j < MaterialEditorGui.currentObject.getTargetCount(); %j++) + { + %target = MaterialEditorGui.currentObject.getTargetName(%j); + %count = SubMaterialSelector.getCount(); + SubMaterialSelector.add(%target); + } + } + else // Other classes that support materials if possible + { + %canSupportMaterial = false; + for( %i = 0; %i < %obj.getFieldCount(); %i++ ) + { + %fieldName = %obj.getField(%i); + + if( %obj.getFieldType(%fieldName) !$= "TypeMaterialName" ) + continue; + + if( !%canSupportMaterial ) + { + MaterialEditorGui.currentObject = %obj; + SubMaterialSelector.clear(); + SubMaterialSelector.add(%fieldName, 0); + } + else + { + %count = SubMaterialSelector.getCount(); + SubMaterialSelector.add(%fieldName, %count); + } + %canSupportMaterial = true; + } + + if( !%canSupportMaterial ) // Non-relevant classes get returned + return; + + MaterialEditorGui.currentMeshMode = "EditorShape"; + MaterialEditorGui.setMode(); + } + + %id = SubMaterialSelector.findText( MaterialEditorGui.currentMaterial.mapTo ); + if( %id != -1 ) + SubMaterialSelector.setSelected( %id ); + else + SubMaterialSelector.setSelected(0); +} + +//============================================================================== +// Helper functions to help create categories and manage category lists + +function MaterialEditorGui::updateAllFields(%this) +{ + matEd_cubemapEd_availableCubemapList.clear(); +} + +function MaterialEditorGui::updatePreviewObject(%this) +{ + %newModel = matEd_quickPreview_Popup.getValue(); + + switch$(%newModel) + { + case "sphere": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/spherePreview.dts"); + matEd_previewObjectView.setOrbitDistance(4); + + case "cube": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/cubePreview.dts"); + matEd_previewObjectView.setOrbitDistance(5); + + case "pyramid": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/pyramidPreview.dts"); + matEd_previewObjectView.setOrbitDistance(5); + + case "cylinder": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/cylinderPreview.dts"); + matEd_previewObjectView.setOrbitDistance(4.2); + + case "torus": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/torusPreview.dts"); + matEd_previewObjectView.setOrbitDistance(4.2); + + case "knot": + matEd_quickPreview_Popup.selected = %newModel; + matEd_previewObjectView.setModel("tools/materialEditor/gui/torusknotPreview.dts"); + } +} + +//============================================================================== +// Helper functions to help load and update the preview and active material + +// Finds the selected line in the material list, then makes it active in the editor. +function MaterialEditorGui::prepareActiveMaterial(%this, %material, %override) +{ + // If were not valid, grab the first valid material out of the materialSet + if( !isObject(%material) ) + %material = MaterialSet.getObject(0); + + // Check made in order to avoid loading the same material. Overriding + // made in special cases + if(%material $= MaterialEditorGui.lastMaterial && !%override) + { + return; + } + else + { + if(MaterialEditorGui.materialDirty ) + { + MaterialEditorGui.showSaveDialog( %material ); + return; + } + + MaterialEditorGui.setActiveMaterial(%material); + } +} + +// Updates the preview material to use the same properties as the selected material, +// and makes that material active in the editor. +function MaterialEditorGui::setActiveMaterial( %this, %material ) +{ + // Warn if selecting a CustomMaterial (they can't be properly previewed or edited) + if ( isObject( %material ) && %material.isMemberOfClass( "CustomMaterial" ) ) + { + MessageBoxOK( "Warning", "The selected Material (" @ %material.getName() @ + ") is a CustomMaterial, and cannot be edited using the Material Editor." ); + return; + } + + MaterialEditorGui.currentMaterial = %material; + MaterialEditorGui.lastMaterial = %material; + + // we create or recreate a material to hold in a pristine state + singleton Material(notDirtyMaterial) + { + mapTo = "matEd_mappedMat"; + diffuseMap[0] = "tools/materialEditor/gui/matEd_mappedMat"; + }; + + // Converts the texture files into absolute paths. + MaterialEditorGui.convertTextureFields(); + + // If we're allowing for name changes, make sure to save the name seperately + %this.originalName = MaterialEditorGui.currentMaterial.name; + + // Copy materials over to other references + MaterialEditorGui.copyMaterials( MaterialEditorGui.currentMaterial, materialEd_previewMaterial ); + MaterialEditorGui.copyMaterials( MaterialEditorGui.currentMaterial, notDirtyMaterial ); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + + // Necessary functionality in order to render correctly + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + + MaterialEditorGui.setMaterialNotDirty(); +} + +function MaterialEditorGui::isMatEditorMaterial(%this, %material) +{ + return ( %material.getFilename() $= "" || + %material.getFilename() $= "tools/gui/materialSelector.ed.gui" || + %material.getFilename() $= "tools/materialEditor/scripts/materialEditor.ed.cs" ); +} + +function MaterialEditorGui::setMaterialNotDirty(%this) +{ + %propertyText = "Material Properties"; + %previewText = "Material Preview"; + MaterialEditorPropertiesWindow.text = %propertyText; + MaterialEditorPreviewWindow.text = %previewText; + + MaterialEditorGui.materialDirty = false; + matEd_PersistMan.removeDirty(MaterialEditorGui.currentMaterial); +} + +function MaterialEditorGui::setMaterialDirty(%this) +{ + %propertyText = "Material Properties *"; + %previewText = "Material Preview *"; + MaterialEditorPropertiesWindow.text = %propertyText; + MaterialEditorPreviewWindow.text = %previewText; + + MaterialEditorGui.materialDirty = true; + + // materials created in the material selector are given that as its filename, so we run another check + if( MaterialEditorGui.isMatEditorMaterial( MaterialEditorGui.currentMaterial ) ) + { + if( MaterialEditorGui.currentMaterial.isAutoGenerated() ) + { + %obj = MaterialEditorGui.currentObject; + + if( %obj.shapeName !$= "" ) + %shapePath = %obj.shapeName; + else if( %obj.isMethod("getDatablock") ) + { + if( %obj.getDatablock().shapeFile !$= "" ) + %shapePath = %obj.getDatablock().shapeFile; + } + + //creating toPath + %k = 0; + while( strpos( %shapePath, "/", %k ) != -1 ) + { + %pos = strpos( %shapePath, "/", %k ); + %k = %pos + 1; + } + %savePath = getSubStr( %shapePath , 0 , %k ); + %savePath = %savePath @ "materials.cs"; + + matEd_PersistMan.setDirty(MaterialEditorGui.currentMaterial, %savePath); + } + else + { + matEd_PersistMan.setDirty(MaterialEditorGui.currentMaterial, "art/materials.cs"); + } + } + else + matEd_PersistMan.setDirty(MaterialEditorGui.currentMaterial); +} + +function MaterialEditorGui::convertTextureFields(%this) +{ + // Find the absolute paths for the texture filenames so that + // we can properly wire up the preview materials and controls. + + for(%diffuseI = 0; %diffuseI < 4; %diffuseI++) + { + %diffuseMap = MaterialEditorGui.currentMaterial.diffuseMap[%diffuseI]; + %diffuseMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %diffuseMap); + MaterialEditorGui.currentMaterial.diffuseMap[%diffuseI] = %diffuseMap; + } + + for(%normalI = 0; %normalI < 4; %normalI++) + { + %normalMap = MaterialEditorGui.currentMaterial.normalMap[%normalI]; + %normalMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %normalMap); + MaterialEditorGui.currentMaterial.normalMap[%normalI] = %normalMap; + } + + for(%overlayI = 0; %overlayI < 4; %overlayI++) + { + %overlayMap = MaterialEditorGui.currentMaterial.overlayMap[%overlayI]; + %overlayMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %overlayMap); + MaterialEditorGui.currentMaterial.overlayMap[%overlayI] = %overlayMap; + } + + for(%detailI = 0; %detailI < 4; %detailI++) + { + %detailMap = MaterialEditorGui.currentMaterial.detailMap[%detailI]; + %detailMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %detailMap); + MaterialEditorGui.currentMaterial.detailMap[%detailI] = %detailMap; + } + + for(%detailNormalI = 0; %detailNormalI < 4; %detailNormalI++) + { + %detailNormalMap = MaterialEditorGui.currentMaterial.detailNormalMap[%detailNormalI]; + %detailNormalMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %detailNormalMap); + MaterialEditorGui.currentMaterial.detailNormalMap[%detailNormalI] = %detailNormalMap; + } + + for(%lightI = 0; %lightI < 4; %lightI++) + { + %lightMap = MaterialEditorGui.currentMaterial.lightMap[%lightI]; + %lightMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %lightMap); + MaterialEditorGui.currentMaterial.lightMap[%lightI] = %lightMap; + } + + for(%toneI = 0; %toneI < 4; %toneI++) + { + %toneMap = MaterialEditorGui.currentMaterial.toneMap[%toneI]; + %toneMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %toneMap); + MaterialEditorGui.currentMaterial.toneMap[%toneI] = %toneMap; + } + + for(%specI = 0; %specI < 4; %specI++) + { + %specMap = MaterialEditorGui.currentMaterial.specularMap[%specI]; + %specMap = MaterialEditorGui.searchForTexture(MaterialEditorGui.currentMaterial, %specMap); + MaterialEditorGui.currentMaterial.specularMap[%specI] = %specMap; + } +} + +// still needs to be optimized further +function MaterialEditorGui::searchForTexture(%this,%material, %texture) +{ + if( %texture !$= "" ) + { + // set the find signal as false to start out with + %isFile = false; + // sete the formats we're going to be looping through if need be + %formats = ".png .jpg .dds .bmp .gif .jng .tga"; + + // if the texture contains the correct filepath and name right off the bat, lets use it + if( isFile(%texture) ) + %isFile = true; + else + { + + for( %i = 0; %i < getWordCount(%formats); %i++) + { + %testFileName = %texture @ getWord( %formats, %i ); + if(isFile(%testFileName)) + { + %isFile = true; + break; + } + } + } + + // if we didn't grab a proper name, lets use a string logarithm + if( !%isFile ) + { + %materialDiffuse = %texture; + %materialDiffuse2 = %texture; + + %materialPath = %material.getFilename(); + + if( strchr( %materialDiffuse, "/") $= "" ) + { + %k = 0; + while( strpos( %materialPath, "/", %k ) != -1 ) + { + %count = strpos( %materialPath, "/", %k ); + %k = %count + 1; + } + + %materialsCs = getSubStr( %materialPath , %k , 99 ); + %texture = strreplace( %materialPath, %materialsCs, %texture ); + } + else + %texture = strreplace( %materialPath, %materialPath, %texture ); + + + // lets test the pathing we came up with + if( isFile(%texture) ) + %isFile = true; + else + { + for( %i = 0; %i < getWordCount(%formats); %i++) + { + %testFileName = %texture @ getWord( %formats, %i ); + if(isFile(%testFileName)) + { + %isFile = true; + break; + } + } + } + + // as a last resort to find the proper name + // we have to resolve using find first file functions very very slow + if( !%isFile ) + { + %k = 0; + while( strpos( %materialDiffuse2, "/", %k ) != -1 ) + { + %count = strpos( %materialDiffuse2, "/", %k ); + %k = %count + 1; + } + + %texture = getSubStr( %materialDiffuse2 , %k , 99 ); + for( %i = 0; %i < getWordCount(%formats); %i++) + { + %searchString = "*" @ %texture @ getWord( %formats, %i ); + %testFileName = findFirstFile( %searchString ); + if( isFile(%testFileName) ) + { + %texture = %testFileName; + %isFile = true; + break; + } + } + } + + return %texture; + } + else + return %texture; //Texture exists and can be found - just return the input argument. + } + + return ""; //No texture associated with this property. +} + +function MaterialEditorGui::updateLivePreview(%this,%preview) +{ + // When checkbox is selected, preview the material in real time, if not; then don't + if( %preview ) + MaterialEditorGui.copyMaterials( materialEd_previewMaterial, MaterialEditorGui.currentMaterial ); + else + MaterialEditorGui.copyMaterials( notDirtyMaterial, MaterialEditorGui.currentMaterial ); + + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); +} + +function MaterialEditorGui::copyMaterials( %this, %copyFrom, %copyTo) +{ + // Make sure we copy and restore the map to. + %mapTo = %copyTo.mapTo; + %copyTo.assignFieldsFrom( %copyFrom ); + %copyTo.mapTo = %mapTo; + +} + +function MaterialEditorGui::guiSync( %this, %material ) +{ + %this.preventUndo = true; + //Setup our headers + if( MaterialEditorGui.currentMode $= "material" ) + { + MatEdMaterialMode-->selMaterialName.setText(MaterialEditorGui.currentMaterial.name); + MatEdMaterialMode-->selMaterialMapTo.setText(MaterialEditorGui.currentMaterial.mapTo); + } + else + { + if( MaterialEditorGui.currentObject.isMethod("getModelFile") ) + { + %sourcePath = MaterialEditorGui.currentObject.getModelFile(); + if( %sourcePath !$= "" ) + { + MatEdTargetMode-->selMaterialMapTo.ToolTip = %sourcePath; + %sourceName = fileName(%sourcePath); + MatEdTargetMode-->selMaterialMapTo.setText(%sourceName); + MatEdTargetMode-->selMaterialName.setText(MaterialEditorGui.currentMaterial.name); + } + } + else + { + %info = MaterialEditorGui.currentObject.getClassName(); + MatEdTargetMode-->selMaterialMapTo.ToolTip = %info; + MatEdTargetMode-->selMaterialMapTo.setText(%info); + MatEdTargetMode-->selMaterialName.setText(MaterialEditorGui.currentMaterial.name); + } + } + + MaterialEditorPropertiesWindow-->alphaRefTextEdit.setText((%material).alphaRef); + MaterialEditorPropertiesWindow-->alphaRefSlider.setValue((%material).alphaRef); + MaterialEditorPropertiesWindow-->doubleSidedCheckBox.setValue((%material).doubleSided); + MaterialEditorPropertiesWindow-->transZWriteCheckBox.setValue((%material).translucentZWrite); + MaterialEditorPropertiesWindow-->alphaTestCheckBox.setValue((%material).alphaTest); + MaterialEditorPropertiesWindow-->castShadows.setValue((%material).castShadows); + MaterialEditorPropertiesWindow-->castDynamicShadows.setValue((%material).castDynamicShadows); + MaterialEditorPropertiesWindow-->translucentCheckbox.setValue((%material).translucent); + + switch$((%material).translucentBlendOp) + { + case "None": %selectedNum = 0; + case "Mul": %selectedNum = 1; + case "Add": %selectedNum = 2; + case "AddAlpha": %selectedNum = 3; + case "Sub": %selectedNum = 4; + case "LerpAlpha": %selectedNum = 5; + } + MaterialEditorPropertiesWindow-->blendingTypePopUp.setSelected(%selectedNum); + + if((%material).cubemap !$= "") + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(1); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.setSelected(1); + } + else if((%material).dynamiccubemap) + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(0); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.setSelected(2); + } + else if((%material).planarReflection) + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(0); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.setSelected(3); + } + else + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(0); + MaterialEditorPropertiesWindow-->reflectionTypePopUp.setSelected(0); + } + + MaterialEditorPropertiesWindow-->effectColor0Swatch.color = (%material).effectColor[0]; + MaterialEditorPropertiesWindow-->effectColor1Swatch.color = (%material).effectColor[1]; + MaterialEditorPropertiesWindow-->showFootprintsCheckbox.setValue((%material).showFootprints); + MaterialEditorPropertiesWindow-->showDustCheckbox.setValue((%material).showDust); + MaterialEditorGui.updateSoundPopup("Footstep", (%material).footstepSoundId, (%material).customFootstepSound); + MaterialEditorGui.updateSoundPopup("Impact", (%material).impactSoundId, (%material).customImpactSound); + + //layer specific controls are located here + %layer = MaterialEditorGui.currentLayer; + + if((%material).diffuseMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->diffuseMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->diffuseMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->diffuseMapNameText.setText( (%material).diffuseMap[%layer] ); + MaterialEditorPropertiesWindow-->diffuseMapDisplayBitmap.setBitmap( (%material).diffuseMap[%layer] ); + } + + if((%material).normalMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->normalMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->normalMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->normalMapNameText.setText( (%material).normalMap[%layer] ); + MaterialEditorPropertiesWindow-->normalMapDisplayBitmap.setBitmap( (%material).normalMap[%layer] ); + } + + if((%material).overlayMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->overlayMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->overlayMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->overlayMapNameText.setText( (%material).overlayMap[%layer] ); + MaterialEditorPropertiesWindow-->overlayMapDisplayBitmap.setBitmap( (%material).overlayMap[%layer] ); + } + + if((%material).detailMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->detailMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->detailMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->detailMapNameText.setText( (%material).detailMap[%layer] ); + MaterialEditorPropertiesWindow-->detailMapDisplayBitmap.setBitmap( (%material).detailMap[%layer] ); + } + + if((%material).detailNormalMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->detailNormalMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->detailNormalMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->detailNormalMapNameText.setText( (%material).detailNormalMap[%layer] ); + MaterialEditorPropertiesWindow-->detailNormalMapDisplayBitmap.setBitmap( (%material).detailNormalMap[%layer] ); + } + + if((%material).lightMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->lightMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->lightMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->lightMapNameText.setText( (%material).lightMap[%layer] ); + MaterialEditorPropertiesWindow-->lightMapDisplayBitmap.setBitmap( (%material).lightMap[%layer] ); + } + + if((%material).toneMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->toneMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->toneMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->toneMapNameText.setText( (%material).toneMap[%layer] ); + MaterialEditorPropertiesWindow-->toneMapDisplayBitmap.setBitmap( (%material).toneMap[%layer] ); + } + + if((%material).specularMap[%layer] $= "") + { + MaterialEditorPropertiesWindow-->specMapNameText.setText( "None" ); + MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap( "tools/materialEditor/gui/unknownImage" ); + } + else + { + MaterialEditorPropertiesWindow-->specMapNameText.setText( (%material).specularMap[%layer] ); + MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap( (%material).specularMap[%layer] ); + } + + MaterialEditorPropertiesWindow-->accuScaleTextEdit.setText((%material).accuScale[%layer]); + MaterialEditorPropertiesWindow-->accuScaleTextEdit.setText((%material).accuScale[%layer]); + MaterialEditorPropertiesWindow-->accuDirectionTextEdit.setText((%material).accuDirection[%layer]); + MaterialEditorPropertiesWindow-->accuDirectionTextEdit.setText((%material).accuDirection[%layer]); + MaterialEditorPropertiesWindow-->accuStrengthTextEdit.setText((%material).accuStrength[%layer]); + MaterialEditorPropertiesWindow-->accuStrengthTextEdit.setText((%material).accuStrength[%layer]); + MaterialEditorPropertiesWindow-->accuCoverageTextEdit.setText((%material).accuCoverage[%layer]); + MaterialEditorPropertiesWindow-->accuCoverageTextEdit.setText((%material).accuCoverage[%layer]); + MaterialEditorPropertiesWindow-->accuSpecularTextEdit.setText((%material).accuSpecular[%layer]); + MaterialEditorPropertiesWindow-->accuSpecularTextEdit.setText((%material).accuSpecular[%layer]); + + MaterialEditorPropertiesWindow-->detailScaleTextEdit.setText( getWord((%material).detailScale[%layer], 0) ); + MaterialEditorPropertiesWindow-->detailNormalStrengthTextEdit.setText( getWord((%material).detailNormalMapStrength[%layer], 0) ); + + MaterialEditorPropertiesWindow-->colorTintSwatch.color = (%material).diffuseColor[%layer]; + MaterialEditorPropertiesWindow-->specularColorSwatch.color = (%material).specular[%layer]; + + MaterialEditorPropertiesWindow-->specularPowerTextEdit.setText((%material).specularPower[%layer]); + MaterialEditorPropertiesWindow-->specularPowerSlider.setValue((%material).specularPower[%layer]); + MaterialEditorPropertiesWindow-->specularStrengthTextEdit.setText((%material).specularStrength[%layer]); + MaterialEditorPropertiesWindow-->specularStrengthSlider.setValue((%material).specularStrength[%layer]); + MaterialEditorPropertiesWindow-->pixelSpecularCheckbox.setValue((%material).pixelSpecular[%layer]); + MaterialEditorPropertiesWindow-->glowCheckbox.setValue((%material).glow[%layer]); + MaterialEditorPropertiesWindow-->emissiveCheckbox.setValue((%material).emissive[%layer]); + MaterialEditorPropertiesWindow-->parallaxTextEdit.setText((%material).parallaxScale[%layer]); + MaterialEditorPropertiesWindow-->parallaxSlider.setValue((%material).parallaxScale[%layer]); + + MaterialEditorPropertiesWindow-->useAnisoCheckbox.setValue((%material).useAnisotropic[%layer]); + MaterialEditorPropertiesWindow-->vertLitCheckbox.setValue((%material).vertLit[%layer]); + MaterialEditorPropertiesWindow-->vertColorSwatch.color = (%material).vertColor[%layer]; + MaterialEditorPropertiesWindow-->subSurfaceCheckbox.setValue((%material).subSurface[%layer]); + + // Animation properties + MaterialEditorPropertiesWindow-->RotationAnimation.setValue(0); + MaterialEditorPropertiesWindow-->ScrollAnimation.setValue(0); + MaterialEditorPropertiesWindow-->WaveAnimation.setValue(0); + MaterialEditorPropertiesWindow-->ScaleAnimation.setValue(0); + MaterialEditorPropertiesWindow-->SequenceAnimation.setValue(0); + + %flags = (%material).getAnimFlags(%layer); + %wordCount = getWordCount( %flags ); + for(%i = 0; %i != %wordCount; %i++) + { + switch$(getWord( %flags, %i)) + { + case "$rotate": MaterialEditorPropertiesWindow-->RotationAnimation.setValue(1); + case "$scroll": MaterialEditorPropertiesWindow-->ScrollAnimation.setValue(1); + case "$wave": MaterialEditorPropertiesWindow-->WaveAnimation.setValue(1); + case "$scale": MaterialEditorPropertiesWindow-->ScaleAnimation.setValue(1); + case "$sequence": MaterialEditorPropertiesWindow-->SequenceAnimation.setValue(1); + } + } + + MaterialEditorPropertiesWindow-->RotationTextEditU.setText( getWord((%material).rotPivotOffset[%layer], 0) ); + MaterialEditorPropertiesWindow-->RotationTextEditV.setText( getWord((%material).rotPivotOffset[%layer], 1) ); + MaterialEditorPropertiesWindow-->RotationSpeedTextEdit.setText( (%material).rotSpeed[%layer] ); + MaterialEditorPropertiesWindow-->RotationSliderU.setValue( getWord((%material).rotPivotOffset[%layer], 0) ); + MaterialEditorPropertiesWindow-->RotationSliderV.setValue( getWord((%material).rotPivotOffset[%layer], 1) ); + MaterialEditorPropertiesWindow-->RotationSpeedSlider.setValue( (%material).rotSpeed[%layer] ); + MaterialEditorPropertiesWindow-->RotationCrosshair.setPosition( 45*mAbs(getWord((%material).rotPivotOffset[%layer], 0))-2, 45*mAbs(getWord((%material).rotPivotOffset[%layer], 1))-2 ); + + MaterialEditorPropertiesWindow-->ScrollTextEditU.setText( getWord((%material).scrollDir[%layer], 0) ); + MaterialEditorPropertiesWindow-->ScrollTextEditV.setText( getWord((%material).scrollDir[%layer], 1) ); + MaterialEditorPropertiesWindow-->ScrollSpeedTextEdit.setText( (%material).scrollSpeed[%layer] ); + MaterialEditorPropertiesWindow-->ScrollSliderU.setValue( getWord((%material).scrollDir[%layer], 0) ); + MaterialEditorPropertiesWindow-->ScrollSliderV.setValue( getWord((%material).scrollDir[%layer], 1) ); + MaterialEditorPropertiesWindow-->ScrollSpeedSlider.setValue( (%material).scrollSpeed[%layer] ); + MaterialEditorPropertiesWindow-->ScrollCrosshair.setPosition( -(23 * getWord((%material).scrollDir[%layer], 0))+20, -(23 * getWord((%material).scrollDir[%layer], 1))+20); + + %waveType = (%material).waveType[%layer]; + for( %radioButton = 0; %radioButton < MaterialEditorPropertiesWindow-->WaveButtonContainer.getCount(); %radioButton++ ) + { + if( %waveType $= MaterialEditorPropertiesWindow-->WaveButtonContainer.getObject(%radioButton).waveType ) + MaterialEditorPropertiesWindow-->WaveButtonContainer.getObject(%radioButton).setStateOn(1); + } + + MaterialEditorPropertiesWindow-->WaveTextEditAmp.setText( (%material).waveAmp[%layer] ); + MaterialEditorPropertiesWindow-->WaveTextEditFreq.setText( (%material).waveFreq[%layer] ); + MaterialEditorPropertiesWindow-->WaveSliderAmp.setValue( (%material).waveAmp[%layer] ); + MaterialEditorPropertiesWindow-->WaveSliderFreq.setValue( (%material).waveFreq[%layer] ); + + %numFrames = mRound( 1 / (%material).sequenceSegmentSize[%layer] ); + + MaterialEditorPropertiesWindow-->SequenceTextEditFPS.setText( (%material).sequenceFramePerSec[%layer] ); + MaterialEditorPropertiesWindow-->SequenceTextEditSSS.setText( %numFrames ); + MaterialEditorPropertiesWindow-->SequenceSliderFPS.setValue( (%material).sequenceFramePerSec[%layer] ); + MaterialEditorPropertiesWindow-->SequenceSliderSSS.setValue( %numFrames ); + + // Accumulation + MaterialEditorPropertiesWindow-->accuCheckbox.setValue((%material).accuEnabled[%layer]); + + %this.preventUndo = false; +} + +//======================================= +// Material Update Functionality + +function MaterialEditorGui::changeLayer( %this, %layer ) +{ + if( MaterialEditorGui.currentLayer == getWord(%layer, 1) ) + return; + + MaterialEditorGui.currentLayer = getWord(%layer, 1); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); +} + +function MaterialEditorGui::updateActiveMaterial(%this, %propertyField, %value, %isSlider, %onMouseUp) +{ + MaterialEditorGui.setMaterialDirty(); + + if(%value $= "") + %value = "\"\""; + + // Here is where we handle undo actions with slider controls. We want to be able to + // undo every onMouseUp; so we overrite the same undo action when necessary in order + // to achieve this desired effect. + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if((%last != -1) && (%last.isSlider) && (!%last.onMouseUp)) + { + %last.field = %propertyField; + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValue = %value; + } + else + { + %action = %this.createUndo(ActionUpdateActiveMaterial, "Update Active Material"); + %action.material = MaterialEditorGui.currentMaterial; + %action.object = MaterialEditorGui.currentObject; + %action.field = %propertyField; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + %action.newValue = %value; + eval( "%action.oldValue = " @ MaterialEditorGui.currentMaterial @ "." @ %propertyField @ ";"); + %action.oldValue = "\"" @ %action.oldValue @ "\""; + MaterialEditorGui.submitUndo( %action ); + } + + eval("materialEd_previewMaterial." @ %propertyField @ " = " @ %value @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("MaterialEditorGui.currentMaterial." @ %propertyField @ " = " @ %value @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } +} + +function MaterialEditorGui::updateActiveMaterialName(%this, %name) +{ + %action = %this.createUndo(ActionUpdateActiveMaterialName, "Update Active Material Name"); + %action.material = MaterialEditorGui.currentMaterial; + %action.object = MaterialEditorGui.currentObject; + %action.oldName = MaterialEditorGui.currentMaterial.getName(); + %action.newName = %name; + MaterialEditorGui.submitUndo( %action ); + + MaterialEditorGui.currentMaterial.setName(%name); + + // Some objects (ConvexShape, DecalRoad etc) reference Materials by name => need + // to find and update all these references so they don't break when we rename the + // Material. + MaterialEditorGui.updateMaterialReferences( MissionGroup, %action.oldName, %action.newName ); +} + +function MaterialEditorGui::updateMaterialReferences( %this, %obj, %oldName, %newName ) +{ + if ( %obj.isMemberOfClass( "SimSet" ) ) + { + // invoke on children + %count = %obj.getCount(); + for ( %i = 0; %i < %count; %i++ ) + %this.updateMaterialReferences( %obj.getObject( %i ), %oldName, %newName ); + } + else + { + %objChanged = false; + + // Change all material fields that use the old material name + %count = %obj.getFieldCount(); + for( %i = 0; %i < %count; %i++ ) + { + %fieldName = %obj.getField( %i ); + if ( ( %obj.getFieldType( %fieldName ) $= "TypeMaterialName" ) && ( %obj.getFieldValue( %fieldName ) $= %oldName ) ) + { + eval( %obj @ "." @ %fieldName @ " = " @ %newName @ ";" ); + %objChanged = true; + } + } + + EWorldEditor.isDirty |= %objChanged; + if ( %objChanged && %obj.isMethod( "postApply" ) ) + %obj.postApply(); + } +} + +// Global Material Options + +function MaterialEditorGui::updateReflectionType( %this, %type ) +{ + if( %type $= "None" ) + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(0); + //Reset material reflection settings on the preview materials + MaterialEditorGui.updateActiveMaterial( "cubeMap", "" ); + MaterialEditorGui.updateActiveMaterial( "dynamicCubemap" , false ); + MaterialEditorGui.updateActiveMaterial( "planarReflection", false ); + } + else + { + if(%type $= "cubeMap") + { + MaterialEditorPropertiesWindow-->matEd_cubemapEditBtn.setVisible(1); + MaterialEditorGui.updateActiveMaterial( %type, materialEd_previewMaterial.cubemap ); + } + else + { + MaterialEditorGui.updateActiveMaterial( %type, true ); + } + } +} + +// Per-Layer Material Options + +// For update maps +// %action : 1 = change map +// %action : 0 = remove map + +function MaterialEditorGui::updateTextureMap( %this, %type, %action ) +{ + %layer = MaterialEditorGui.currentLayer; + + %bitmapCtrl = MaterialEditorPropertiesWindow.findObjectByInternalName( %type @ "MapDisplayBitmap", true ); + %textCtrl = MaterialEditorPropertiesWindow.findObjectByInternalName( %type @ "MapNameText", true ); + + if( %action ) + { + %texture = MaterialEditorGui.openFile("texture"); + if( %texture !$= "" ) + { + %bitmapCtrl.setBitmap(%texture); + + %bitmap = %bitmapCtrl.bitmap; + %bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/",""); + %bitmapCtrl.setBitmap(%bitmap); + %textCtrl.setText(%bitmap); + MaterialEditorGui.updateActiveMaterial(%type @ "Map[" @ %layer @ "]","\"" @ %bitmap @ "\""); + } + } + else + { + %textCtrl.setText("None"); + %bitmapCtrl.setBitmap("tools/materialEditor/gui/unknownImage"); + MaterialEditorGui.updateActiveMaterial(%type @ "Map[" @ %layer @ "]",""); + } +} + +function MaterialEditorGui::updateDetailScale(%this,%newScale) +{ + %layer = MaterialEditorGui.currentLayer; + + %detailScale = "\"" @ %newScale SPC %newScale @ "\""; + MaterialEditorGui.updateActiveMaterial("detailScale[" @ %layer @ "]", %detailScale); +} + +function MaterialEditorGui::updateDetailNormalStrength(%this,%newStrength) +{ + %layer = MaterialEditorGui.currentLayer; + + %detailStrength = "\"" @ %newStrength @ "\""; + MaterialEditorGui.updateActiveMaterial("detailNormalMapStrength[" @ %layer @ "]", %detailStrength); +} + +function MaterialEditorGui::updateSpecMap(%this,%action) +{ + %layer = MaterialEditorGui.currentLayer; + + if( %action ) + { + %texture = MaterialEditorGui.openFile("texture"); + if( %texture !$= "" ) + { + MaterialEditorGui.updateActiveMaterial("pixelSpecular[" @ MaterialEditorGui.currentLayer @ "]", 0); + + MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap(%texture); + + %bitmap = MaterialEditorPropertiesWindow-->specMapDisplayBitmap.bitmap; + %bitmap = strreplace(%bitmap,"tools/materialEditor/scripts/",""); + MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap(%bitmap); + MaterialEditorPropertiesWindow-->specMapNameText.setText(%bitmap); + MaterialEditorGui.updateActiveMaterial("specularMap[" @ %layer @ "]","\"" @ %bitmap @ "\""); + } + } + else + { + MaterialEditorPropertiesWindow-->specMapNameText.setText("None"); + MaterialEditorPropertiesWindow-->specMapDisplayBitmap.setBitmap("tools/materialEditor/gui/unknownImage"); + MaterialEditorGui.updateActiveMaterial("specularMap[" @ %layer @ "]",""); + } + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); +} + +function MaterialEditorGui::updateRotationOffset(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %X = MaterialEditorPropertiesWindow-->RotationTextEditU.getText(); + %Y = MaterialEditorPropertiesWindow-->RotationTextEditV.getText(); + MaterialEditorPropertiesWindow-->RotationCrosshair.setPosition(45*mAbs(%X)-2, 45*mAbs(%Y)-2); + + MaterialEditorGui.updateActiveMaterial("rotPivotOffset[" @ %layer @ "]","\"" @ %X SPC %Y @ "\"",%isSlider,%onMouseUp); +} + +function MaterialEditorGui::updateRotationSpeed(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %speed = MaterialEditorPropertiesWindow-->RotationSpeedTextEdit.getText(); + MaterialEditorGui.updateActiveMaterial("rotSpeed[" @ %layer @ "]",%speed,%isSlider,%onMouseUp); +} + +function MaterialEditorGui::updateScrollOffset(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %X = MaterialEditorPropertiesWindow-->ScrollTextEditU.getText(); + %Y = MaterialEditorPropertiesWindow-->ScrollTextEditV.getText(); + MaterialEditorPropertiesWindow-->ScrollCrosshair.setPosition( -(23 * %X)+20, -(23 * %Y)+20); + + MaterialEditorGui.updateActiveMaterial("scrollDir[" @ %layer @ "]","\"" @ %X SPC %Y @ "\"",%isSlider,%onMouseUp); +} + +function MaterialEditorGui::updateScrollSpeed(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %speed = MaterialEditorPropertiesWindow-->ScrollSpeedTextEdit.getText(); + MaterialEditorGui.updateActiveMaterial("scrollSpeed[" @ %layer @ "]",%speed,%isSlider,%onMouseUp); +} + +function MaterialEditorGui::updateWaveType(%this) +{ + for( %radioButton = 0; %radioButton < MaterialEditorPropertiesWindow-->WaveButtonContainer.getCount(); %radioButton++ ) + { + if( MaterialEditorPropertiesWindow-->WaveButtonContainer.getObject(%radioButton).getValue() == 1 ) + %type = MaterialEditorPropertiesWindow-->WaveButtonContainer.getObject(%radioButton).waveType; + } + + %layer = MaterialEditorGui.currentLayer; + MaterialEditorGui.updateActiveMaterial("waveType[" @ %layer @ "]", %type); +} + +function MaterialEditorGui::updateWaveAmp(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %amp = MaterialEditorPropertiesWindow-->WaveTextEditAmp.getText(); + MaterialEditorGui.updateActiveMaterial("waveAmp[" @ %layer @ "]", %amp, %isSlider, %onMouseUp); +} + +function MaterialEditorGui::updateWaveFreq(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %freq = MaterialEditorPropertiesWindow-->WaveTextEditFreq.getText(); + MaterialEditorGui.updateActiveMaterial("waveFreq[" @ %layer @ "]", %freq, %isSlider, %onMouseUp); +} + +function MaterialEditorGui::updateSequenceFPS(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %fps = MaterialEditorPropertiesWindow-->SequenceTextEditFPS.getText(); + MaterialEditorGui.updateActiveMaterial("sequenceFramePerSec[" @ %layer @ "]", %fps, %isSlider, %onMouseUp); +} + +function MaterialEditorGui::updateSequenceSSS(%this, %isSlider, %onMouseUp) +{ + %layer = MaterialEditorGui.currentLayer; + %sss = 1 / MaterialEditorPropertiesWindow-->SequenceTextEditSSS.getText(); + MaterialEditorGui.updateActiveMaterial("sequenceSegmentSize[" @ %layer @ "]", %sss, %isSlider, %onMouseUp); +} + +function MaterialEditorGui::updateAnimationFlags(%this) +{ + MaterialEditorGui.setMaterialDirty(); + %single = true; + + if(MaterialEditorPropertiesWindow-->RotationAnimation.getValue() == true) + { + if(%single == true) + %flags = %flags @ "$Rotate"; + else + %flags = %flags @ " | $Rotate"; + + %single = false; + } + if(MaterialEditorPropertiesWindow-->ScrollAnimation.getValue() == true) + { + if(%single == true) + %flags = %flags @ "$Scroll"; + else + %flags = %flags @ " | $Scroll"; + + %single = false; + } + if(MaterialEditorPropertiesWindow-->WaveAnimation.getValue() == true) + { + if(%single == true) + %flags = %flags @ "$Wave"; + else + %flags = %flags @ " | $Wave"; + + %single = false; + } + if(MaterialEditorPropertiesWindow-->ScaleAnimation.getValue() == true) + { + if(%single == true) + %flags = %flags @ "$Scale"; + else + %flags = %flags @ " | $Scale"; + + %single = false; + } + if(MaterialEditorPropertiesWindow-->SequenceAnimation.getValue() == true) + { + if(%single == true) + %flags = %flags @ "$Sequence"; + else + %flags = %flags @ " | $Sequence"; + + %single = false; + } + + if(%flags $= "") + %flags = "\"\""; + + %action = %this.createUndo(ActionUpdateActiveMaterialAnimationFlags, "Update Active Material"); + %action.material = MaterialEditorGui.currentMaterial; + %action.object = MaterialEditorGui.currentObject; + %action.layer = MaterialEditorGui.currentLayer; + + %action.newValue = %flags; + + %oldFlags = MaterialEditorGui.currentMaterial.getAnimFlags(MaterialEditorGui.currentLayer); + if(%oldFlags $= "") + %oldFlags = "\"\""; + + %action.oldValue = %oldFlags; + MaterialEditorGui.submitUndo( %action ); + + eval("materialEd_previewMaterial.animFlags[" @ MaterialEditorGui.currentLayer @ "] = " @ %flags @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("MaterialEditorGui.currentMaterial.animFlags[" @ MaterialEditorGui.currentLayer @ "] = " @ %flags @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } +} + +//============================================================================== +// Color Picker Helpers - They are all using colorPicker.ed.gui in order to function +// These functions are mainly passed callbacks from getColorI/getColorF callbacks + +function MaterialEditorGui::syncGuiColor(%this, %guiCtrl, %propname, %color) +{ + %layer = MaterialEditorGui.currentLayer; + + %r = getWord(%color,0); + %g = getWord(%color,1); + %b = getWord(%color,2); + %a = getWord(%color,3); + + %colorSwatch = (%r SPC %g SPC %b SPC %a); + %color = "\"" @ %r SPC %g SPC %b SPC %a @ "\""; + + %guiCtrl.color = %colorSwatch; + MaterialEditorGui.updateActiveMaterial(%propName, %color); +} + +//These two functions are focused on object/layer specific functionality +function MaterialEditorGui::updateColorMultiply(%this,%color) +{ + %propName = "diffuseColor[" @ MaterialEditorGui.currentLayer @ "]"; + %this.syncGuiColor(MaterialEditorPropertiesWindow-->colorTintSwatch, %propName, %color); +} + +function MaterialEditorGui::updateSpecularCheckbox(%this,%value) +{ + MaterialEditorGui.updateActiveMaterial("pixelSpecular[" @ MaterialEditorGui.currentLayer @ "]", %value); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); +} + +function MaterialEditorGui::updateSpecular(%this, %color) +{ + %propName = "specular[" @ MaterialEditorGui.currentLayer @ "]"; + %this.syncGuiColor(MaterialEditorPropertiesWindow-->specularColorSwatch, %propName, %color); +} + +function MaterialEditorGui::updateSubSurfaceColor(%this, %color) +{ + %propName = "subSurfaceColor[" @ MaterialEditorGui.currentLayer @ "]"; + %this.syncGuiColor(MaterialEditorPropertiesWindow-->subSurfaceColorSwatch, %propName, %color); +} + +function MaterialEditorGui::updateEffectColor0(%this, %color) +{ + %this.syncGuiColor(MaterialEditorPropertiesWindow-->effectColor0Swatch, "effectColor[0]", %color); +} + +function MaterialEditorGui::updateEffectColor1(%this, %color) +{ + %this.syncGuiColor(MaterialEditorPropertiesWindow-->effectColor1Swatch, "effectColor[1]", %color); +} + +function MaterialEditorGui::updateBehaviorSound(%this, %type, %sound) +{ + %defaultId = -1; + %customName = ""; + + switch$ (%sound) + { + case "<Soft>": %defaultId = 0; + case "<Hard>": %defaultId = 1; + case "<Metal>": %defaultId = 2; + case "<Snow>": %defaultId = 3; + default: %customName = %sound; + } + + %this.updateActiveMaterial(%type @ "SoundId", %defaultId); + %this.updateActiveMaterial("custom" @ %type @ "Sound", %customName); +} + +function MaterialEditorGui::updateSoundPopup(%this, %type, %defaultId, %customName) +{ + %ctrl = MaterialEditorPropertiesWindow.findObjectByInternalName( %type @ "SoundPopup", true ); + + switch (%defaultId) + { + case 0: %name = "<Soft>"; + case 1: %name = "<Hard>"; + case 2: %name = "<Metal>"; + case 3: %name = "<Snow>"; + default: + if (%customName $= "") + %name = "<None>"; + else + %name = %customName; + } + + %r = %ctrl.findText(%name); + if (%r != -1) + %ctrl.setSelected(%r, false); + else + %ctrl.setText(%name); +} + +//These two functions are focused on environment specific functionality +function MaterialEditorGui::updateLightColor(%this, %color) +{ + matEd_previewObjectView.setLightColor(%color); + matEd_lightColorPicker.color = %color; +} + +function MaterialEditorGui::updatePreviewBackground(%this,%color) +{ + matEd_previewBackground.color = %color; + MaterialPreviewBackgroundPicker.color = %color; +} + +function MaterialEditorGui::updateAmbientColor(%this,%color) +{ + matEd_previewObjectView.setAmbientLightColor(%color); + matEd_ambientLightColorPicker.color = %color; +} + +//============================================================================== +// Commands for the Cubemap Editor + +function MaterialEditorGui::selectCubemap(%this) +{ + %cubemap = MaterialEditorGui.currentCubemap; + if(!isObject(%cubemap)) + return; + + MaterialEditorGui.updateActiveMaterial( "cubemap", %cubemap.name ); + MaterialEditorGui.hideCubemapEditor(); +} + +function MaterialEditorGui::cancelCubemap(%this) +{ + %cubemap = MaterialEditorGui.currentCubemap; + + %idx = matEd_cubemapEd_availableCubemapList.findItemText( %cubemap.getName() ); + matEd_cubemapEd_availableCubemapList.setItemText( %idx, notDirtyCubemap.originalName ); + %cubemap.setName( notDirtyCubemap.originalName ); + + MaterialEditorGui.copyCubemaps( notDirtyCubemap, %cubemap ); + MaterialEditorGui.copyCubemaps( notDirtyCubemap, matEdCubeMapPreviewMat); + + %cubemap.updateFaces(); + matEdCubeMapPreviewMat.updateFaces(); +} + +function MaterialEditorGui::showCubemapEditor(%this) +{ + if (matEd_cubemapEditor.isVisible()) + return; + + MaterialEditorGui.currentCubemap = ""; + + matEd_cubemapEditor.setVisible(1); + new PersistenceManager(matEd_cubemapEdPerMan); + MaterialEditorGui.setCubemapNotDirty(); + + for( %i = 0; %i < RootGroup.getCount(); %i++ ) + { + if( RootGroup.getObject(%i).getClassName()!$= "CubemapData" ) + continue; + + for( %k = 0; %k < UnlistedCubemaps.count(); %k++ ) + { + %unlistedFound = 0; + if( UnlistedCubemaps.getValue(%k) $= RootGroup.getObject(%i).name ) + { + %unlistedFound = 1; + break; + } + } + + if( %unlistedFound ) + continue; + + matEd_cubemapEd_availableCubemapList.addItem( RootGroup.getObject(%i).name ); + } + + singleton CubemapData(notDirtyCubemap); + + // if there was no cubemap, pick the first, select, and bail, these are going to take + // care of themselves in the selected function + if( !isObject( MaterialEditorGui.currentMaterial.cubemap ) ) + { + if( matEd_cubemapEd_availableCubemapList.getItemCount() > 0 ) + { + matEd_cubemapEd_availableCubemapList.setSelected(0, true); + return; + } + else + { + // if there are no cubemaps, then create one, select, and bail + %cubemap = MaterialEditorGui.createNewCubemap(); + matEd_cubemapEd_availableCubemapList.addItem( %cubemap.name ); + matEd_cubemapEd_availableCubemapList.setSelected(0, true); + return; + } + } + + // do not directly change activeMat! + MaterialEditorGui.currentCubemap = MaterialEditorGui.currentMaterial.cubemap.getId(); + %cubemap = MaterialEditorGui.currentCubemap; + + notDirtyCubemap.originalName = %cubemap.getName(); + MaterialEditorGui.copyCubemaps( %cubemap, notDirtyCubemap); + MaterialEditorGui.copyCubemaps( %cubemap, matEdCubeMapPreviewMat); + MaterialEditorGui.syncCubemap( %cubemap ); +} + +function MaterialEditorGui::hideCubemapEditor(%this,%cancel) +{ + if(%cancel) + MaterialEditorGui.cancelCubemap(); + + matEd_cubemapEd_availableCubemapList.clearItems(); + matEd_cubemapEdPerMan.delete(); + matEd_cubemapEditor.setVisible(0); +} + +// create category and update current material if there is one +function MaterialEditorGui::addCubemap( %this,%cubemapName ) +{ + if( %cubemapName $= "" ) + { + MessageBoxOK( "Error", "Can not create a cubemap without a valid name."); + return; + } + + for(%i = 0; %i < RootGroup.getCount(); %i++) + { + if( %cubemapName $= RootGroup.getObject(%i).getName() ) + { + MessageBoxOK( "Error", "There is already an object with the same name."); + return; + } + } + + // Create and select a new cubemap + %cubemap = MaterialEditorGui.createNewCubemap( %cubemapName ); + %idx = matEd_cubemapEd_availableCubemapList.addItem( %cubemap.name ); + matEd_cubemapEd_availableCubemapList.setSelected( %idx, true ); + + // material category text field to blank + matEd_addCubemapWindow-->cubemapName.setText(""); +} + +function MaterialEditorGui::createNewCubemap( %this, %cubemap ) +{ + if( %cubemap $= "" ) + { + for(%i = 0; ; %i++) + { + %cubemap = "newCubemap_" @ %i; + if( !isObject(%cubemap) ) + break; + } + } + + new CubemapData(%cubemap) + { + cubeFace[0] = "tools/materialEditor/gui/cube_xNeg"; + cubeFace[1] = "tools/materialEditor/gui/cube_xPos"; + cubeFace[2] = "tools/materialEditor/gui/cube_ZNeg"; + cubeFace[3] = "tools/materialEditor/gui/cube_ZPos"; + cubeFace[4] = "tools/materialEditor/gui/cube_YNeg"; + cubeFace[5] = "tools/materialEditor/gui/cube_YPos"; + + parentGroup = RootGroup; + }; + + matEd_cubemapEdPerMan.setDirty( %cubemap, "art/materials.cs" ); + matEd_cubemapEdPerMan.saveDirty(); + + return %cubemap; +} + +function MaterialEditorGui::setCubemapDirty(%this) +{ + %propertyText = "Create Cubemap *"; + matEd_cubemapEditor.text = %propertyText; + matEd_cubemapEditor.dirty = true; + matEd_cubemapEditor-->saveCubemap.setActive(true); + + %cubemap = MaterialEditorGui.currentCubemap; + + // materials created in the materail selector are given that as its filename, so we run another check + if( MaterialEditorGui.isMatEditorMaterial( %cubemap ) ) + matEd_cubemapEdPerMan.setDirty(%cubemap, "art/materials.cs"); + else + matEd_cubemapEdPerMan.setDirty(%cubemap); +} + +function MaterialEditorGui::setCubemapNotDirty(%this) +{ + %propertyText= strreplace("Create Cubemap" , "*" , ""); + matEd_cubemapEditor.text = %propertyText; + matEd_cubemapEditor.dirty = false; + matEd_cubemapEditor-->saveCubemap.setActive(false); + + %cubemap = MaterialEditorGui.currentCubemap; + matEd_cubemapEdPerMan.removeDirty(%cubemap); +} + +function MaterialEditorGui::showDeleteCubemapDialog(%this) +{ + %idx = matEd_cubemapEd_availableCubemapList.getSelectedItem(); + %cubemap = matEd_cubemapEd_availableCubemapList.getItemText( %idx ); + %cubemap = %cubemap.getId(); + + if( %cubemap == -1 || !isObject(%cubemap) ) + return; + + if( isObject( %cubemap ) ) + { + MessageBoxYesNoCancel("Delete Cubemap?", + "Are you sure you want to delete<br><br>" @ %cubemap.getName() @ "<br><br> Cubemap deletion won't take affect until the engine is quit.", + "MaterialEditorGui.deleteCubemap( " @ %cubemap @ ", " @ %idx @ " );", + "", + "" ); + } +} + +function MaterialEditorGui::deleteCubemap( %this, %cubemap, %idx ) +{ + matEd_cubemapEd_availableCubemapList.deleteItem( %idx ); + + UnlistedCubemaps.add( "unlistedCubemaps", %cubemap.getName() ); + + if( !MaterialEditorGui.isMatEditorMaterial( %cubemap ) ) + { + matEd_cubemapEdPerMan.removeDirty( %cubemap ); + matEd_cubemapEdPerMan.removeObjectFromFile( %cubemap ); + } + + if( matEd_cubemapEd_availableCubemapList.getItemCount() > 0 ) + { + matEd_cubemapEd_availableCubemapList.setSelected(0, true); + } + else + { + // if there are no cubemaps, then create one, select, and bail + %cubemap = MaterialEditorGui.createNewCubemap(); + matEd_cubemapEd_availableCubemapList.addItem( %cubemap.getName() ); + matEd_cubemapEd_availableCubemapList.setSelected(0, true); + } +} + +function matEd_cubemapEd_availableCubemapList::onSelect( %this, %id, %cubemap ) +{ + %cubemap = %cubemap.getId(); + if( MaterialEditorGui.currentCubemap $= %cubemap ) + return; + + if( matEd_cubemapEditor.dirty ) + { + %savedCubemap = MaterialEditorGui.currentCubemap; + MessageBoxYesNoCancel("Save Existing Cubemap?", + "Do you want to save changes to <br><br>" @ %savedCubemap.getName(), + "MaterialEditorGui.saveCubemap(" @ true @ ");", + "MaterialEditorGui.saveCubemapDialogDontSave(" @ %cubemap @ ");", + "MaterialEditorGui.saveCubemapDialogCancel();" ); + } + else + MaterialEditorGui.changeCubemap( %cubemap ); +} + +function MaterialEditorGui::showSaveCubemapDialog( %this ) +{ + %cubemap = MaterialEditorGui.currentCubemap; + if( !isObject(%cubemap) ) + return; + + MessageBoxYesNoCancel("Save Cubemap?", + "Do you want to save changes to <br><br>" @ %cubemap.getName(), + "MaterialEditorGui.saveCubemap( " @ %cubemap @ " );", + "", + "" ); +} + +function MaterialEditorGui::saveCubemap( %this, %cubemap ) +{ + notDirtyCubemap.originalName = %cubemap.getName(); + MaterialEditorGui.copyCubemaps( %cubemap, notDirtyCubemap ); + MaterialEditorGui.copyCubemaps( %cubemap, matEdCubeMapPreviewMat); + + matEd_cubemapEdPerMan.saveDirty(); + + MaterialEditorGui.setCubemapNotDirty(); +} + +function MaterialEditorGui::saveCubemapDialogDontSave( %this, %newCubemap) +{ + //deal with old cubemap first + %oldCubemap = MaterialEditorGui.currentCubemap; + + %idx = matEd_cubemapEd_availableCubemapList.findItemText( %oldCubemap.getName() ); + matEd_cubemapEd_availableCubemapList.setItemText( %idx, notDirtyCubemap.originalName ); + %oldCubemap.setName( notDirtyCubemap.originalName ); + + MaterialEditorGui.copyCubemaps( notDirtyCubemap, %oldCubemap); + MaterialEditorGui.copyCubemaps( notDirtyCubemap, matEdCubeMapPreviewMat); + MaterialEditorGui.syncCubemap( %oldCubemap ); + + MaterialEditorGui.changeCubemap( %newCubemap ); +} + +function MaterialEditorGui::saveCubemapDialogCancel( %this ) +{ + %cubemap = MaterialEditorGui.currentCubemap; + %idx = matEd_cubemapEd_availableCubemapList.findItemText( %cubemap.getName() ); + matEd_cubemapEd_availableCubemapList.clearSelection(); + matEd_cubemapEd_availableCubemapList.setSelected( %idx, true ); +} + +function MaterialEditorGui::changeCubemap( %this, %cubemap ) +{ + MaterialEditorGui.setCubemapNotDirty(); + MaterialEditorGui.currentCubemap = %cubemap; + + notDirtyCubemap.originalName = %cubemap.getName(); + MaterialEditorGui.copyCubemaps( %cubemap, notDirtyCubemap); + MaterialEditorGui.copyCubemaps( %cubemap, matEdCubeMapPreviewMat); + MaterialEditorGui.syncCubemap( %cubemap ); +} + +function MaterialEditorGui::editCubemapImage( %this, %face ) +{ + MaterialEditorGui.setCubemapDirty(); + + %cubemap = MaterialEditorGui.currentCubemap; + %bitmap = MaterialEditorGui.openFile("texture"); + if( %bitmap !$= "" && %bitmap !$= "tools/materialEditor/gui/cubemapBtnBorder" ) + { + %cubemap.cubeFace[%face] = %bitmap; + MaterialEditorGui.copyCubemaps( %cubemap, matEdCubeMapPreviewMat); + MaterialEditorGui.syncCubemap( %cubemap ); + } +} + +function MaterialEditorGui::editCubemapName( %this, %newName ) +{ + MaterialEditorGui.setCubemapDirty(); + + %cubemap = MaterialEditorGui.currentCubemap; + + %idx = matEd_cubemapEd_availableCubemapList.findItemText( %cubemap.getName() ); + matEd_cubemapEd_availableCubemapList.setItemText( %idx, %newName ); + %cubemap.setName(%newName); + + MaterialEditorGui.syncCubemap( %cubemap ); +} + +function MaterialEditorGui::syncCubemap( %this, %cubemap ) +{ + %xpos = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[0]); + if( %xpos !$= "" ) + matEd_cubemapEd_XPos.setBitmap( %xpos ); + + %xneg = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[1]); + if( %xneg !$= "" ) + matEd_cubemapEd_XNeg.setBitmap( %xneg ); + + %yneg = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[2]); + if( %yneg !$= "" ) + matEd_cubemapEd_YNeG.setBitmap( %yneg ); + + %ypos = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[3]); + if( %ypos !$= "" ) + matEd_cubemapEd_YPos.setBitmap( %ypos ); + + %zpos = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[4]); + if( %zpos !$= "" ) + matEd_cubemapEd_ZPos.setBitmap( %zpos ); + + %zneg = MaterialEditorGui.searchForTexture(%cubemap.getName(), %cubemap.cubeFace[5]); + if( %zneg !$= "" ) + matEd_cubemapEd_ZNeg.setBitmap( %zneg ); + + matEd_cubemapEd_activeCubemapNameTxt.setText(%cubemap.getName()); + + %cubemap.updateFaces(); + matEdCubeMapPreviewMat.updateFaces(); +} + +function MaterialEditorGui::copyCubemaps( %this, %copyFrom, %copyTo) +{ + %copyTo.cubeFace[0] = %copyFrom.cubeFace[0]; + %copyTo.cubeFace[1] = %copyFrom.cubeFace[1]; + %copyTo.cubeFace[2] = %copyFrom.cubeFace[2]; + %copyTo.cubeFace[3] = %copyFrom.cubeFace[3]; + %copyTo.cubeFace[4] = %copyFrom.cubeFace[4]; + %copyTo.cubeFace[5] = %copyFrom.cubeFace[5]; +} + + +//============================================================================== +// showSaveDialog logic + +function MaterialEditorGui::showSaveDialog( %this, %toMaterial ) +{ + MessageBoxYesNoCancel("Save Material?", + "The material " @ MaterialEditorGui.currentMaterial.getName() @ " has unsaved changes. <br>Do you want to save?", + "MaterialEditorGui.saveDialogSave(" @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogDontSave(" @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogCancel();" ); +} + +function MaterialEditorGui::showMaterialChangeSaveDialog( %this, %toMaterial ) +{ + %fromMaterial = MaterialEditorGui.currentMaterial; + + MessageBoxYesNoCancel("Save Material?", + "The material " @ %fromMaterial.getName() @ " has unsaved changes. <br>Do you want to save before changing the material?", + "MaterialEditorGui.saveDialogSave(" @ %toMaterial @ "); MaterialEditorGui.changeMaterial(" @ %fromMaterial @ ", " @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogDontSave(" @ %toMaterial @ "); MaterialEditorGui.changeMaterial(" @ %fromMaterial @ ", " @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogCancel();" ); +} + +/* +function MaterialEditorGui::showCreateNewMaterialSaveDialog( %this, %toMaterial ) +{ + MessageBoxYesNoCancel("Save Material?", + "The material " @ MaterialEditorGui.currentMaterial.getName() @ " has unsaved changes. <br>Do you want to save before changing the material?", + "MaterialEditorGui.save(); MaterialEditorGui.createNewMaterial(" @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogDontSave(" @ %toMaterial @ "); MaterialEditorGui.changeMaterial(" @ %toMaterial @ ");", + "MaterialEditorGui.saveDialogCancel();" ); +} +*/ + +function MaterialEditorGui::saveDialogCancel( %this ) +{ + MaterialEditorGui.guiSync( materialEd_previewMaterial ); +} + +function MaterialEditorGui::saveDialogDontSave( %this, %material ) +{ + MaterialEditorGui.currentMaterial.setName( %this.originalName ); + + //restore to defaults + MaterialEditorGui.copyMaterials( notDirtyMaterial, MaterialEditorGui.currentMaterial ); + MaterialEditorGui.copyMaterials( notDirtyMaterial, materialEd_previewMaterial ); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + + MaterialEditorGui.setMaterialNotDirty(); + + MaterialEditorGui.setActiveMaterial( %material ); +} + +function MaterialEditorGui::saveDialogSave( %this, %material ) +{ + MaterialEditorGui.save(); + MaterialEditorGui.setActiveMaterial( %material ); +} + +function MaterialEditorGui::save( %this ) +{ + if( MaterialEditorGui.currentMaterial.getName() $= "" ) + { + MessageBoxOK("Cannot perform operation", "Saved materials cannot be named \"\". A name must be given before operation is performed" ); + return; + } + + // Update the live object regardless in this case + MaterialEditorGui.updateLivePreview(true); + + %currentMaterial = MaterialEditorGui.currentMaterial; + if( %currentMaterial == -1 ) + { + MessageBoxOK("Cannot perform operation", "Could not locate material" ); + return; + } + + // Specifically for materials autogenerated from shapes. + if( %currentMaterial.isAutoGenerated() ) + %currentMaterial.setAutoGenerated( false ); + + // Save the material using the persistence manager + matEd_PersistMan.saveDirty(); + + // Clean up the Material Editor + MaterialEditorGui.copyMaterials( materialEd_previewMaterial, notDirtyMaterial ); + MaterialEditorGui.setMaterialNotDirty(); +} + +//============================================================================== +// Create New and Delete Material + +function MaterialEditorGui::createNewMaterial( %this ) +{ + %action = %this.createUndo(ActionCreateNewMaterial, "Create New Material"); + %action.object = ""; + + %material = getUniqueName( "newMaterial" ); + new Material(%material) + { + diffuseMap[0] = "core/art/warnmat"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + + %action.newMaterial = %material.getId(); + %action.oldMaterial = MaterialEditorGui.currentMaterial; + + MaterialEditorGui.submitUndo( %action ); + + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.prepareActiveMaterial( %material.getId(), true ); +} + +function MaterialEditorGui::deleteMaterial( %this ) +{ + %action = %this.createUndo(ActionDeleteMaterial, "Delete Material"); + %action.object = MaterialEditorGui.currentObject; + %action.currentMode = MaterialEditorGui.currentMode; + + /* + if( MaterialEditorGui.currentMode $= "Mesh" ) + { + %materialTarget = SubMaterialSelector.text; + %action.materialTarget = %materialTarget; + + //create the stub material + %toMaterial = getUniqueName( "newMaterial" ); + new Material(%toMaterial) + { + diffuseMap[0] = "core/art/warnmat"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + + %action.toMaterial = %toMaterial.getId(); + %action.fromMaterial = MaterialEditorGui.currentMaterial; + %action.fromMaterialOldFname = MaterialEditorGui.currentMaterial.getFilename(); + } + else + { + // Grab first material we see; if theres not one, create one + %toMaterial = MaterialSet.getObject(0); + if( !isObject( %toMaterial ) ) + { + %toMaterial = getUniqueName( "newMaterial" ); + new Material(%toMaterial) + { + diffuseMap[0] = "core/art/warnmat"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + } + + %action.toMaterial = %toMaterial.getId(); + %action.fromMaterial = MaterialEditorGui.currentMaterial; + } + */ + + // Grab first material we see; if theres not one, create one + %newMaterial = getUniqueName( "newMaterial" ); + new Material(%newMaterial) + { + diffuseMap[0] = "core/art/warnmat"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + + // Setup vars + %action.newMaterial = %newMaterial.getId(); + %action.oldMaterial = MaterialEditorGui.currentMaterial; + %action.oldMaterialFname = MaterialEditorGui.currentMaterial.getFilename(); + + // Submit undo + MaterialEditorGui.submitUndo( %action ); + + // Delete the material from file + if( !MaterialEditorGui.isMatEditorMaterial( MaterialEditorGui.currentMaterial ) ) + { + matEd_PersistMan.removeObjectFromFile(MaterialEditorGui.currentMaterial); + matEd_PersistMan.removeDirty(MaterialEditorGui.currentMaterial); + } + + // Delete the material as seen through the material selector. + UnlistedMaterials.add( "unlistedMaterials", MaterialEditorGui.currentMaterial.getName() ); + + // Loadup another material + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.prepareActiveMaterial( %newMaterial.getId(), true ); +} + +//============================================================================== +// Clear and Refresh Material + +function MaterialEditorGui::clearMaterial(%this) +{ + %action = %this.createUndo(ActionClearMaterial, "Clear Material"); + %action.material = MaterialEditorGui.currentMaterial; + %action.object = MaterialEditorGui.currentObject; + + pushInstantGroup(); + %action.oldMaterial = new Material(); + %action.newMaterial = new Material(); + popInstantGroup(); + + MaterialEditorGui.submitUndo( %action ); + + MaterialEditorGui.copyMaterials( MaterialEditorGui.currentMaterial, %action.oldMaterial ); + + %tempMat = new Material() + { + name = "tempMaterial"; + mapTo = "unmapped_mat"; + parentGroup = RootGroup; + }; + + MaterialEditorGui.copyMaterials( %tempMat, materialEd_previewMaterial ); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( %tempMat, MaterialEditorGui.currentMaterial ); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + + MaterialEditorGui.setMaterialDirty(); + + %tempMat.delete(); +} + +function MaterialEditorGui::refreshMaterial(%this) +{ + %action = %this.createUndo(ActionRefreshMaterial, "Refresh Material"); + %action.material = MaterialEditorGui.currentMaterial; + %action.object = MaterialEditorGui.currentObject; + + pushInstantGroup(); + %action.oldMaterial = new Material(); + %action.newMaterial = new Material(); + popInstantGroup(); + + MaterialEditorGui.copyMaterials( MaterialEditorGui.currentMaterial, %action.oldMaterial ); + MaterialEditorGui.copyMaterials( notDirtyMaterial, %action.newMaterial ); + + %action.oldName = MaterialEditorGui.currentMaterial.getName(); + %action.newName = %this.originalName; + + MaterialEditorGui.submitUndo( %action ); + + MaterialEditorGui.currentMaterial.setName( %this.originalName ); + MaterialEditorGui.copyMaterials( notDirtyMaterial, materialEd_previewMaterial ); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( notDirtyMaterial, MaterialEditorGui.currentMaterial ); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + + MaterialEditorGui.setMaterialNotDirty(); +} + +//============================================================================== +// Switching and Changing Materials + +function MaterialEditorGui::switchMaterial( %this, %material ) +{ + //MaterialEditorGui.currentMaterial = %material.getId(); + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.prepareActiveMaterial( %material.getId(), true ); +} + +/*------------------------------------------------------------------------------ + This changes the map to's of possibly two materials (%fromMaterial, %toMaterial) + and updates the engines libraries accordingly in order to make this change per + object/per objects instances/per target. Before this functionality is enacted, + there is a popup beforehand that will ask if you are sure if you want to make + this change. Making this change will physically alter possibly two materials.cs + files in order to move the (%fromMaterial, %toMaterial), replacing the + (%fromMaterials)'s mapTo to "unmapped_mat". +-------------------------------------------------------------------------------*/ + +function MaterialEditorGui::changeMaterial(%this, %fromMaterial, %toMaterial) +{ + %action = %this.createUndo(ActionChangeMaterial, "Change Material"); + %action.object = MaterialEditorGui.currentObject; + + %materialTarget = SubMaterialSelector.text; + %action.materialTarget = %materialTarget; + + %action.fromMaterial = %fromMaterial; + %action.toMaterial = %toMaterial; + %action.toMaterialOldFname = %toMaterial.getFilename(); + %action.object = MaterialEditorGui.currentObject; + + if( MaterialEditorGui.currentMeshMode $= "Model" ) // Models + { + %action.mode = "model"; + + MaterialEditorGui.currentObject.changeMaterial( %materialTarget, %fromMaterial.getName(), %toMaterial.getName() ); + + if( MaterialEditorGui.currentObject.shapeName !$= "" ) + %sourcePath = MaterialEditorGui.currentObject.shapeName; + else if( MaterialEditorGui.currentObject.isMethod("getDatablock") ) + { + if( MaterialEditorGui.currentObject.getDatablock().shapeFile !$= "" ) + %sourcePath = MaterialEditorGui.currentObject.getDatablock().shapeFile; + } + + // Creating "to" path + %k = 0; + while( strpos( %sourcePath, "/", %k ) != -1 ) + { + %count = strpos( %sourcePath, "/", %k ); + %k = %count + 1; + } + %fileName = getSubStr( %sourcePath , 0 , %k ); + %fileName = %fileName @ "materials.cs"; + + %action.toMaterialNewFname = %fileName; + + MaterialEditorGui.prepareActiveMaterial( %toMaterial, true ); + if( !MaterialEditorGui.isMatEditorMaterial( %toMaterial ) ) + { + matEd_PersistMan.removeObjectFromFile(%toMaterial); + } + + matEd_PersistMan.setDirty(%fromMaterial); + matEd_PersistMan.setDirty(%toMaterial, %fileName); + matEd_PersistMan.saveDirty(); + + matEd_PersistMan.removeDirty(%fromMaterial); + matEd_PersistMan.removeDirty(%toMaterial); + } + else // EditorShapes + { + %action.mode = "editorShapes"; + + eval("MaterialEditorGui.currentObject." @ SubMaterialSelector.getText() @ " = " @ %toMaterial.getName() @ ";"); + if( MaterialEditorGui.currentObject.isMethod("postApply") ) + MaterialEditorGui.currentObject.postApply(); + + MaterialEditorGui.prepareActiveMaterial( %toMaterial, true ); + } + + MaterialEditorGui.submitUndo( %action ); +} + +//============================================================================== +// Image thumbnail right-clicks. + +// not yet functional +function MaterialEditorMapThumbnail::onRightClick( %this ) +{ + if( !isObject( "MaterialEditorMapThumbnailPopup" ) ) + new PopupMenu( MaterialEditorMapThumbnailPopup ) + { + superClass = "MenuBuilder"; + isPopup = true; + + item[ 0 ] = "Open File" TAB "" TAB "openFile( %this.filePath );"; + item[ 1 ] = "Open Folder" TAB "" TAB "openFolder( filePath( %this.filePath ) );"; + + filePath = ""; + }; + + // Find the text control containing the filename. + + %textCtrl = %this.parentGroup.findObjectByInternalName( %this.fileNameTextCtrl, true ); + if( !%textCtrl ) + return; + + %fileName = %textCtrl.getText(); + %fullPath = makeFullPath( %fileName, getMainDotCsDir() ); + + // Construct a full path. + + %isValid = isFile( %fullPath ); + if( !%isValid ) + { + if( isFile( %fileName ) ) + { + %fullPath = %fileName; + %isValid = true; + } + else + { + // Try material-relative path. + + %material = MaterialEditorGui.currentMaterial; + if( isObject( %material ) ) + { + %materialPath = filePath( makeFullPath( %material.getFilename(), getMainDotCsDir() ) ); + %fullPath = makeFullPath( %fileName, %materialPath ); + %isValid = isFile( %fullPath ); + } + } + } + + %popup = MaterialEditorMapThumbnailPopup; + %popup.enableItem( 0, %isValid ); + %popup.enableItem( 1, %isValid ); + %popup.filePath = %fullPath; + + %popup.showPopup( Canvas ); +} + +// Accumulation +function MaterialEditorGui::updateAccuCheckbox(%this, %value) +{ + MaterialEditorGui.updateActiveMaterial("accuEnabled[" @ MaterialEditorGui.currentLayer @ "]", %value); + MaterialEditorGui.guiSync( materialEd_previewMaterial ); +} diff --git a/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditorUndo.ed.cs b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditorUndo.ed.cs new file mode 100644 index 000000000..184f02ce4 --- /dev/null +++ b/Templates/BaseGame/game/tools/materialEditor/scripts/materialEditorUndo.ed.cs @@ -0,0 +1,477 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function MaterialEditorGui::createUndo(%this, %class, %desc) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = %class; + superClass = BaseMaterialEdAction; + actionName = %desc; + }; + popInstantGroup(); + return %action; +} + +function MaterialEditorGui::submitUndo(%this, %action) +{ + if(!%this.preventUndo) + %action.addToManager(Editor.getUndoManager()); +} + +function BaseMaterialEdAction::redo(%this) +{ + %this.redo(); +} + +function BaseMaterialEdAction::undo(%this) +{ +} + +// Generic updateActiveMaterial redo/undo + +function ActionUpdateActiveMaterial::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + /* + if( MaterialEditorGui.currentMaterial != %this.material ) + { + MaterialEditorGui.currentObject = %this.object; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.material); + } + */ + eval("materialEd_previewMaterial." @ %this.field @ " = " @ %this.newValue @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("%this.material." @ %this.field @ " = " @ %this.newValue @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + + MaterialEditorGui.preventUndo = true; + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + MaterialEditorGui.preventUndo = false; + } + else + { + eval("%this.material." @ %this.field @ " = " @ %this.newValue @ ";"); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionUpdateActiveMaterial::undo(%this) +{ + MaterialEditorGui.preventUndo = true; + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + /* + if( MaterialEditorGui.currentMaterial != %this.material ) + { + MaterialEditorGui.currentObject = %this.object; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.material); + } + */ + + eval("materialEd_previewMaterial." @ %this.field @ " = " @ %this.oldValue @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("%this.material." @ %this.field @ " = " @ %this.oldValue @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + eval("%this.material." @ %this.field @ " = " @ %this.oldValue @ ";"); + %this.material.flush(); + %this.material.reload(); + } + + MaterialEditorGui.preventUndo = false; +} + +// Special case updateActiveMaterial redo/undo + +function ActionUpdateActiveMaterialAnimationFlags::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + /* + if( MaterialEditorGui.currentMaterial != %this.material ) + { + MaterialEditorGui.currentObject = %this.object; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.material); + } + */ + + eval("materialEd_previewMaterial.animFlags[" @ %this.layer @ "] = " @ %this.newValue @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("%this.material.animFlags[" @ %this.layer @ "] = " @ %this.newValue @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + eval("%this.material.animFlags[" @ %this.layer @ "] = " @ %this.newValue @ ";"); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionUpdateActiveMaterialAnimationFlags::undo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + eval("materialEd_previewMaterial.animFlags[" @ %this.layer @ "] = " @ %this.oldValue @ ";"); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + eval("%this.material.animFlags[" @ %this.layer @ "] = " @ %this.oldValue @ ";"); + MaterialEditorGui.currentMaterial.flush(); + MaterialEditorGui.currentMaterial.reload(); + } + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + eval("%this.material.animFlags[" @ %this.layer @ "] = " @ %this.oldValue @ ";"); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionUpdateActiveMaterialName::redo(%this) +{ + %this.material.setName(%this.newName); + MaterialEditorGui.updateMaterialReferences( MissionGroup, %this.oldName, %this.newName ); + + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } +} + +function ActionUpdateActiveMaterialName::undo(%this) +{ + %this.material.setName(%this.oldName); + MaterialEditorGui.updateMaterialReferences( MissionGroup, %this.newName, %this.oldName ); + + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } +} + +function ActionRefreshMaterial::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + %this.material.setName( %this.newName ); + + MaterialEditorGui.copyMaterials( %this.newMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( %this.newMaterial , %this.material ); + %this.material.flush(); + %this.material.reload(); + } + + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialNotDirty(); + } + else + { + MaterialEditorGui.copyMaterials( %this.newMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionRefreshMaterial::undo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + %this.material.setName( %this.oldName ); + + MaterialEditorGui.copyMaterials( %this.oldMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( %this.oldMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + MaterialEditorGui.copyMaterials( %this.oldMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionClearMaterial::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + MaterialEditorGui.copyMaterials( %this.newMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( %this.newMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + MaterialEditorGui.copyMaterials( %this.newMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionClearMaterial::undo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() && MaterialEditorGui.currentMaterial == %this.material ) + { + MaterialEditorGui.copyMaterials( %this.oldMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + if (MaterialEditorGui.livePreview == true) + { + MaterialEditorGui.copyMaterials( %this.oldMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + MaterialEditorGui.setMaterialDirty(); + } + else + { + MaterialEditorGui.copyMaterials( %this.oldMaterial, %this.material ); + %this.material.flush(); + %this.material.reload(); + } +} + +function ActionChangeMaterial::redo(%this) +{ + if( %this.mode $= "model" ) + { + %this.object.changeMaterial( %this.materialTarget, %this.fromMaterial.getName(), %this.toMaterial.getName() ); + + MaterialEditorGui.currentObject = %this.object; + + if( %this.toMaterial.getFilename() !$= "tools/gui/materialSelector.ed.gui" || + %this.toMaterial.getFilename() !$= "tools/materialEditor/scripts/materialEditor.ed.cs") + { + matEd_PersistMan.removeObjectFromFile(%this.toMaterial); + } + + matEd_PersistMan.setDirty(%this.fromMaterial); + matEd_PersistMan.setDirty(%this.toMaterial, %this.toMaterialNewFname); + matEd_PersistMan.saveDirty(); + + matEd_PersistMan.removeDirty(%this.fromMaterial); + matEd_PersistMan.removeDirty(%this.toMaterial); + } + else + { + eval("%this.object." @ %this.materialTarget @ " = " @ %this.toMaterial.getName() @ ";"); + MaterialEditorGui.currentObject.postApply(); + } + + if( MaterialEditorPreviewWindow.isVisible() ) + MaterialEditorGui.setActiveMaterial( %this.toMaterial ); +} + +function ActionChangeMaterial::undo(%this) +{ + if( %this.mode $= "model" ) + { + %this.object.changeMaterial( %this.materialTarget, %this.toMaterial.getName(), %this.fromMaterial.getName() ); + + MaterialEditorGui.currentObject = %this.object; + + if( %this.toMaterial.getFilename() !$= "tools/gui/materialSelector.ed.gui" || + %this.toMaterial.getFilename() !$= "tools/materialEditor/scripts/materialEditor.ed.cs") + { + matEd_PersistMan.removeObjectFromFile(%this.toMaterial); + } + + + matEd_PersistMan.setDirty(%this.fromMaterial); + matEd_PersistMan.setDirty(%this.toMaterial, %this.toMaterialOldFname); + matEd_PersistMan.saveDirty(); + + matEd_PersistMan.removeDirty(%this.fromMaterial); + matEd_PersistMan.removeDirty(%this.toMaterial); + } + else + { + eval("%this.object." @ %this.materialTarget @ " = " @ %this.fromMaterial.getName() @ ";"); + MaterialEditorGui.currentObject.postApply(); + } + + if( MaterialEditorPreviewWindow.isVisible() ) + MaterialEditorGui.setActiveMaterial( %this.fromMaterial ); +} + +function ActionCreateNewMaterial::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() ) + { + if( MaterialEditorGui.currentMaterial != %this.newMaterial ) + { + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.newMaterial); + } + + MaterialEditorGui.copyMaterials( %this.newMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + } + + %idx = UnlistedMaterials.getIndexFromValue( %this.newMaterial.getName() ); + UnlistedMaterials.erase( %idx ); +} + +function ActionCreateNewMaterial::undo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() ) + { + if( MaterialEditorGui.currentMaterial != %this.oldMaterial ) + { + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.oldMaterial); + } + + MaterialEditorGui.copyMaterials( %this.oldMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + } + + UnlistedMaterials.add( "unlistedMaterials", %this.newMaterial.getName() ); +} + +function ActionDeleteMaterial::redo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() ) + { + if( MaterialEditorGui.currentMaterial != %this.newMaterial ) + { + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.newMaterial); + } + + MaterialEditorGui.copyMaterials( %this.newMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + } + + if( %this.oldMaterial.getFilename() !$= "tools/gui/materialSelector.ed.gui" || + %this.oldMaterial.getFilename() !$= "tools/materialEditor/scripts/materialEditor.ed.cs") + { + matEd_PersistMan.removeObjectFromFile(%this.oldMaterial); + } + + UnlistedMaterials.add( "unlistedMaterials", %this.oldMaterial.getName() ); +} + +function ActionDeleteMaterial::undo(%this) +{ + if( MaterialEditorPreviewWindow.isVisible() ) + { + if( MaterialEditorGui.currentMaterial != %this.oldMaterial ) + { + MaterialEditorGui.currentObject = ""; + MaterialEditorGui.setMode(); + MaterialEditorGui.setActiveMaterial(%this.oldMaterial); + } + + MaterialEditorGui.copyMaterials( %this.oldMaterial, materialEd_previewMaterial ); + materialEd_previewMaterial.flush(); + materialEd_previewMaterial.reload(); + + MaterialEditorGui.guiSync( materialEd_previewMaterial ); + } + + matEd_PersistMan.setDirty(%this.oldMaterial, %this.oldMaterialFname); + matEd_PersistMan.saveDirty(); + matEd_PersistMan.removeDirty(%this.oldMaterial); + + %idx = UnlistedMaterials.getIndexFromValue( %this.oldMaterial.getName() ); + UnlistedMaterials.erase( %idx ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/main.cs b/Templates/BaseGame/game/tools/meshRoadEditor/main.cs new file mode 100644 index 000000000..d101e50b0 --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/main.cs @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeMeshRoadEditor() +{ + echo(" % - Initializing Mesh Road Editor"); + + exec( "./meshRoadEditor.cs" ); + exec( "./meshRoadEditorGui.gui" ); + exec( "./meshRoadEditorToolbar.gui"); + exec( "./meshRoadEditorGui.cs" ); + + MeshRoadEditorGui.setVisible( false ); + MeshRoadEditorOptionsWindow.setVisible( false ); + MeshRoadEditorToolbar.setVisible( false ); + MeshRoadEditorTreeWindow.setVisible( false ); + + EditorGui.add( MeshRoadEditorGui ); + EditorGui.add( MeshRoadEditorOptionsWindow ); + EditorGui.add( MeshRoadEditorToolbar ); + EditorGui.add( MeshRoadEditorTreeWindow ); + + new ScriptObject( MeshRoadEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = MeshRoadEditorGui; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "backspace", "MeshRoadEditorGui.deleteNode();", "" ); + %map.bindCmd( keyboard, "1", "MeshRoadEditorGui.prepSelectionMode();", "" ); + %map.bindCmd( keyboard, "2", "ToolsPaletteArray->MeshRoadEditorMoveMode.performClick();", "" ); + %map.bindCmd( keyboard, "3", "ToolsPaletteArray->MeshRoadEditorRotateMode.performClick();", "" ); + %map.bindCmd( keyboard, "4", "ToolsPaletteArray->MeshRoadEditorScaleMode.performClick();", "" ); + %map.bindCmd( keyboard, "5", "ToolsPaletteArray->MeshRoadEditorAddRoadMode.performClick();", "" ); + %map.bindCmd( keyboard, "=", "ToolsPaletteArray->MeshRoadEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadadd", "ToolsPaletteArray->MeshRoadEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "-", "ToolsPaletteArray->MeshRoadEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadminus", "ToolsPaletteArray->MeshRoadEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "z", "MeshRoadEditorShowSplineBtn.performClick();", "" ); + %map.bindCmd( keyboard, "x", "MeshRoadEditorWireframeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "v", "MeshRoadEditorShowRoadBtn.performClick();", "" ); + MeshRoadEditorPlugin.map = %map; + + MeshRoadEditorPlugin.initSettings(); +} + +function destroyMeshRoadEditor() +{ +} + +function MeshRoadEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Mesh Road Editor", "", MeshRoadEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Mesh Road Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "MeshRoadEditorPlugin", "MeshRoadEditorPalette", expandFilename("tools/worldEditor/images/toolbar/mesh-road-editor"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( MeshRoadEditorOptionsWindow, MeshRoadEditorTreeWindow); + + // Add ourselves to the Editor Settings window + exec( "./meshRoadEditorSettingsTab.gui" ); + ESettingsWindow.addTabPage( EMeshRoadEditorSettingsPage ); +} + +function MeshRoadEditorPlugin::onActivated( %this ) +{ + %this.readSettings(); + + ToolsPaletteArray->MeshRoadEditorAddRoadMode.performClick(); + EditorGui.bringToFront( MeshRoadEditorGui ); + MeshRoadEditorGui.setVisible( true ); + MeshRoadEditorGui.makeFirstResponder( true ); + MeshRoadEditorOptionsWindow.setVisible( true ); + MeshRoadEditorToolbar.setVisible( true ); + MeshRoadEditorTreeWindow.setVisible( true ); + MeshRoadTreeView.open(ServerMeshRoadSet,true); + %this.map.push(); + + // Store this on a dynamic field + // in order to restore whatever setting + // the user had before. + %this.prevGizmoAlignment = GlobalGizmoProfile.alignment; + + // The DecalEditor always uses Object alignment. + GlobalGizmoProfile.alignment = "Object"; + + // Set the status bar here until all tool have been hooked up + EditorGuiStatusBar.setInfo("Mesh road editor."); + EditorGuiStatusBar.setSelection(""); + + Parent::onActivated(%this); +} + +function MeshRoadEditorPlugin::onDeactivated( %this ) +{ + %this.writeSettings(); + + MeshRoadEditorGui.setVisible( false ); + MeshRoadEditorOptionsWindow.setVisible( false ); + MeshRoadEditorToolbar.setVisible( false ); + MeshRoadEditorTreeWindow.setVisible( false ); + %this.map.pop(); + + // Restore the previous Gizmo + // alignment settings. + GlobalGizmoProfile.alignment = %this.prevGizmoAlignment; + + Parent::onDeactivated(%this); +} + +function MeshRoadEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %hasSelection = false; + + if( isObject( MeshRoadEditorGui.road ) ) + %hasSelection = true; + + %editMenu.enableItem( 3, false ); // Cut + %editMenu.enableItem( 4, false ); // Copy + %editMenu.enableItem( 5, false ); // Paste + %editMenu.enableItem( 6, %hasSelection ); // Delete + %editMenu.enableItem( 8, false ); // Deselect +} + +function MeshRoadEditorPlugin::handleDelete( %this ) +{ + MeshRoadEditorGui.deleteNode(); +} + +function MeshRoadEditorPlugin::handleEscape( %this ) +{ + return MeshRoadEditorGui.onEscapePressed(); +} + +function MeshRoadEditorPlugin::isDirty( %this ) +{ + return MeshRoadEditorGui.isDirty; +} + +function MeshRoadEditorPlugin::onSaveMission( %this, %missionFile ) +{ + if( MeshRoadEditorGui.isDirty ) + { + MissionGroup.save( %missionFile ); + MeshRoadEditorGui.isDirty = false; + } +} + +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function MeshRoadEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "MeshRoadEditor", true ); + + EditorSettings.setDefaultValue( "DefaultWidth", "10" ); + EditorSettings.setDefaultValue( "DefaultDepth", "5" ); + EditorSettings.setDefaultValue( "DefaultNormal", "0 0 1" ); + EditorSettings.setDefaultValue( "HoverSplineColor", "255 0 0 255" ); + EditorSettings.setDefaultValue( "SelectedSplineColor", "0 255 0 255" ); + EditorSettings.setDefaultValue( "HoverNodeColor", "255 255 255 255" ); //<-- Not currently used + EditorSettings.setDefaultValue( "TopMaterialName", "DefaultRoadMaterialTop" ); + EditorSettings.setDefaultValue( "BottomMaterialName", "DefaultRoadMaterialOther" ); + EditorSettings.setDefaultValue( "SideMaterialName", "DefaultRoadMaterialOther" ); + + EditorSettings.endGroup(); +} + +function MeshRoadEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "MeshRoadEditor", true ); + + MeshRoadEditorGui.DefaultWidth = EditorSettings.value("DefaultWidth"); + MeshRoadEditorGui.DefaultDepth = EditorSettings.value("DefaultDepth"); + MeshRoadEditorGui.DefaultNormal = EditorSettings.value("DefaultNormal"); + MeshRoadEditorGui.HoverSplineColor = EditorSettings.value("HoverSplineColor"); + MeshRoadEditorGui.SelectedSplineColor = EditorSettings.value("SelectedSplineColor"); + MeshRoadEditorGui.HoverNodeColor = EditorSettings.value("HoverNodeColor"); + MeshRoadEditorGui.topMaterialName = EditorSettings.value("TopMaterialName"); + MeshRoadEditorGui.bottomMaterialName = EditorSettings.value("BottomMaterialName"); + MeshRoadEditorGui.sideMaterialName = EditorSettings.value("SideMaterialName"); + + EditorSettings.endGroup(); +} + +function MeshRoadEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "MeshRoadEditor", true ); + + EditorSettings.setValue( "DefaultWidth", MeshRoadEditorGui.DefaultWidth ); + EditorSettings.setValue( "DefaultDepth", MeshRoadEditorGui.DefaultDepth ); + EditorSettings.setValue( "DefaultNormal", MeshRoadEditorGui.DefaultNormal ); + EditorSettings.setValue( "HoverSplineColor", MeshRoadEditorGui.HoverSplineColor ); + EditorSettings.setValue( "SelectedSplineColor", MeshRoadEditorGui.SelectedSplineColor ); + EditorSettings.setValue( "HoverNodeColor", MeshRoadEditorGui.HoverNodeColor ); + EditorSettings.setValue( "TopMaterialName", MeshRoadEditorGui.topMaterialName ); + EditorSettings.setValue( "BottomMaterialName", MeshRoadEditorGui.bottomMaterialName ); + EditorSettings.setValue( "SideMaterialName", MeshRoadEditorGui.sideMaterialName ); + + EditorSettings.endGroup(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditor.cs b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditor.cs new file mode 100644 index 000000000..112562d82 --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditor.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile( MeshRoadEditorProfile ) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192 192"; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiDisabledTextEditProfile) +{ + opaque = false; + border = 0; + bitmap = "./textEdit"; + borderColor = "255 255 255 200"; + fontColor = "0 0 0"; + fontColorHL = "255 255 255"; + fontColorNA = "128 128 128"; + textOffset = "4 2"; + autoSizeWidth = false; + autoSizeHeight = false; + tab = false; + canKeyFocus = false; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiSimpleBorderProfile) +{ + opaque = false; + border = 1; + category = "Editor"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.cs b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.cs new file mode 100644 index 000000000..9d2985e25 --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.cs @@ -0,0 +1,256 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$MeshRoad::wireframe = true; +$MeshRoad::showSpline = true; +$MeshRoad::showReflectPlane = false; +$MeshRoad::showRoad = true; +$MeshRoad::breakAngle = 3.0; + +function MeshRoadEditorGui::onWake( %this ) +{ + $MeshRoad::EditorOpen = true; + + %count = EWorldEditor.getSelectionSize(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = EWorldEditor.getSelectedObject(%i); + if ( %obj.getClassName() !$= "MeshRoad" ) + EWorldEditor.unselectObject(); + else + %this.setSelectedRoad( %obj ); + } + + //%this-->TabBook.selectPage(0); + + %this.onNodeSelected(-1); +} + +function MeshRoadEditorGui::onSleep( %this ) +{ + $MeshRoad::EditorOpen = false; +} + +function MeshRoadEditorGui::paletteSync( %this, %mode ) +{ + %evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);"; + eval(%evalShortcut); +} +function MeshRoadEditorGui::onEscapePressed( %this ) +{ + if( %this.getMode() $= "MeshRoadEditorAddNodeMode" ) + { + %this.prepSelectionMode(); + return true; + } + return false; +} +function MeshRoadEditorGui::onRoadSelected( %this, %road ) +{ + %this.road = %road; + + // Update the materialEditorList + if( isObject( %road ) ) + $Tools::materialEditorList = %road.getId(); + + MeshRoadInspector.inspect( %road ); + MeshRoadTreeView.buildVisibleTree(true); + if( MeshRoadTreeView.getSelectedObject() != %road ) + { + MeshRoadTreeView.clearSelection(); + %treeId = MeshRoadTreeView.findItemByObjectId( %road ); + MeshRoadTreeView.selectItem( %treeId ); + } +} + +function MeshRoadEditorGui::onNodeSelected( %this, %nodeIdx ) +{ + if ( %nodeIdx == -1 ) + { + MeshRoadEditorOptionsWindow-->position.setActive( false ); + MeshRoadEditorOptionsWindow-->position.setValue( "" ); + + MeshRoadEditorOptionsWindow-->rotation.setActive( false ); + MeshRoadEditorOptionsWindow-->rotation.setValue( "" ); + + MeshRoadEditorOptionsWindow-->width.setActive( false ); + MeshRoadEditorOptionsWindow-->width.setValue( "" ); + + MeshRoadEditorOptionsWindow-->depth.setActive( false ); + MeshRoadEditorOptionsWindow-->depth.setValue( "" ); + } + else + { + MeshRoadEditorOptionsWindow-->position.setActive( true ); + MeshRoadEditorOptionsWindow-->position.setValue( %this.getNodePosition() ); + + MeshRoadEditorOptionsWindow-->rotation.setActive( true ); + MeshRoadEditorOptionsWindow-->rotation.setValue( %this.getNodeNormal() ); + + MeshRoadEditorOptionsWindow-->width.setActive( true ); + MeshRoadEditorOptionsWindow-->width.setValue( %this.getNodeWidth() ); + + MeshRoadEditorOptionsWindow-->depth.setActive( true ); + MeshRoadEditorOptionsWindow-->depth.setValue( %this.getNodeDepth() ); + } +} + + +function MeshRoadEditorGui::onNodeModified( %this, %nodeIdx ) +{ + MeshRoadEditorOptionsWindow-->position.setValue( %this.getNodePosition() ); + MeshRoadEditorOptionsWindow-->rotation.setValue( %this.getNodeNormal() ); + MeshRoadEditorOptionsWindow-->width.setValue( %this.getNodeWidth() ); + MeshRoadEditorOptionsWindow-->depth.setValue( %this.getNodeDepth() ); +} + +function MeshRoadEditorGui::editNodeDetails( %this ) +{ + + %this.setNodePosition( MeshRoadEditorOptionsWindow-->position.getText() ); + %this.setNodeNormal( MeshRoadEditorOptionsWindow-->rotation.getText() ); + %this.setNodeWidth( MeshRoadEditorOptionsWindow-->width.getText() ); + %this.setNodeDepth( MeshRoadEditorOptionsWindow-->depth.getText() ); +} + +function MeshRoadEditorGui::onBrowseClicked( %this ) +{ + //%filename = RETextureFileCtrl.getText(); + + %dlg = new OpenFileDialog() + { + Filters = "All Files (*.*)|*.*|"; + DefaultPath = MeshRoadEditorGui.lastPath; + DefaultFile = %filename; + ChangePath = false; + MustExist = true; + }; + + %ret = %dlg.Execute(); + if(%ret) + { + MeshRoadEditorGui.lastPath = filePath( %dlg.FileName ); + %filename = %dlg.FileName; + MeshRoadEditorGui.setTextureFile( %filename ); + MeshRoadEditorTextureFileCtrl.setText( %filename ); + } + + %dlg.delete(); +} + +function MeshRoadInspector::inspect( %this, %obj ) +{ + %name = ""; + if ( isObject( %obj ) ) + %name = %obj.getName(); + else + MeshFieldInfoControl.setText( "" ); + + //RiverInspectorNameEdit.setValue( %name ); + Parent::inspect( %this, %obj ); +} + +function MeshRoadInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); +} + +function MeshRoadInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + MeshFieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +function MeshRoadTreeView::onInspect(%this, %obj) +{ + MeshRoadInspector.inspect(%obj); +} + +function MeshRoadTreeView::onSelect(%this, %obj) +{ + MeshRoadEditorGui.road = %obj; + MeshRoadInspector.inspect( %obj ); + if(%obj != MeshRoadEditorGui.getSelectedRoad()) + { + MeshRoadEditorGui.setSelectedRoad( %obj ); + } +} + +function MeshRoadEditorGui::prepSelectionMode( %this ) +{ + %mode = %this.getMode(); + + if ( %mode $= "MeshRoadEditorAddNodeMode" ) + { + if ( isObject( %this.getSelectedRoad() ) ) + %this.deleteNode(); + } + + %this.setMode( "MeshRoadEditorSelectMode" ); + ToolsPaletteArray-->MeshRoadEditorSelectMode.setStateOn(1); +} + +//------------------------------------------------------------------------------ +function EMeshRoadEditorSelectModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorAddModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorMoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorRotateModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorScaleModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorInsertModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function EMeshRoadEditorRemoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function MeshRoadDefaultWidthSliderCtrlContainer::onWake(%this) +{ + MeshRoadDefaultWidthSliderCtrlContainer-->slider.setValue(MeshRoadDefaultWidthTextEditContainer-->textEdit.getText()); +} + +function MeshRoadDefaultDepthSliderCtrlContainer::onWake(%this) +{ + MeshRoadDefaultDepthSliderCtrlContainer-->slider.setValue(MeshRoadDefaultDepthTextEditContainer-->textEdit.getText()); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.gui b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.gui new file mode 100644 index 000000000..14bc92d2a --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorGui.gui @@ -0,0 +1,361 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiMeshRoadEditorCtrl(MeshRoadEditorGui,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "MeshRoadEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + DefaultWidth = "10"; + HoverSplineColor = "0 255 0 255"; + SelectedSplineColor = "255 0 255 255"; + HoverNodeColor = "255 255 255 255"; + + new GuiWindowCollapseCtrl(MeshRoadEditorTreeWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 167"; + MinExtent = "210 100"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EditorGui.setEditor( WorldEditorInspectorPlugin );"; + EdgeSnap = "1"; + text = "Mesh Roads"; + + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "5 25"; + Extent = "200 120"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 118"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(MeshRoadTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + }; + }; + new GuiWindowCollapseCtrl(MeshRoadEditorOptionsWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(MeshRoadEditorTreeWindow.extent, 1) - 2; + Extent = "210 530"; + MinExtent = "210 298"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EditorGui.setEditor( WorldEditorPlugin );"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(){ //Node Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "202 85"; + Docking = "Top"; + Margin = "3 3 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "86 18"; + text = "Node Properties"; + }; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 21"; + Extent = "46 18"; + text = "Position"; + }; + new GuiTextEditCtrl(){ + internalName = "position"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 21"; + Extent = "141 18"; + text = ""; + AltCommand = "MeshRoadEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 42"; + Extent = "46 18"; + text = "Rotation"; + }; + new GuiTextEditCtrl(){ + internalName = "rotation"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 42"; + Extent = "141 18"; + text = ""; + AltCommand = "MeshRoadEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 63"; + Extent = "46 18"; + text = "Width"; + }; + new GuiTextEditCtrl(){ + internalName = "width"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "57 63"; + Extent = "52 18"; + text = ""; + AltCommand = "MeshRoadEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "110 63"; + Extent = "32 18"; + text = "Depth"; + }; + new GuiTextEditCtrl(){ + internalName = "depth"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "146 63"; + Extent = "52 18"; + text = ""; + AltCommand = "MeshRoadEditorGui.editNodeDetails();"; + }; + }; + new GuiContainer(){ //Mesh Road Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 112"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "Mesh Road Properties"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 129"; + Extent = "202 357"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "-14 41 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(MeshRoadInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + name = "MeshRoadInspector"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "179 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + }; + }; + new GuiMLTextCtrl(MeshFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 485"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorSettingsTab.gui b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorSettingsTab.gui new file mode 100644 index 000000000..81485d307 --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorSettingsTab.gui @@ -0,0 +1,697 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(MeshRoadEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EMeshRoadEditorSettingsPage) { + fitBook = "1"; + text = "Mesh Road Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Defaults"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Width:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/DefaultWidth"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Depth:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/DefaultDepth"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Normal:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/DefaultNormal"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Top Material:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/TopMaterialName"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Bottom Material:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/BottomMaterialName"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Side Material:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/SideMaterialName"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Colors"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/HoverSplineColor"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Hover Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "4 2"; + extent = "70 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "MeshRoadEditorPlugin.readSettings();"; + editorSettingsValue = "MeshRoadEditor/SelectedSplineColor"; + editorSettingsWrite = "MeshRoadEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sel. Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "4 2"; + extent = "70 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorToolbar.gui b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorToolbar.gui new file mode 100644 index 000000000..e0bf56309 --- /dev/null +++ b/Templates/BaseGame/game/tools/meshRoadEditor/meshRoadEditorToolbar.gui @@ -0,0 +1,322 @@ +%guiContent = new GuiControl(MeshRoadEditorToolbar,EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 32"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 6"; + extent = "100 20"; + minExtent = "8 8"; + visible = "1"; + text = "Mesh Road Settings"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiDynamicCtrlArrayControl(){ + Position = "116 3"; + extent = "111 32"; + colCount = "31"; + colSize = "29"; + rowCount = "1"; + RowSize = "27"; + rowSpacing = "2"; + colspacing = "4"; + + new GuiBitmapButtonCtrl(MeshRoadEditorShowSplineBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "167 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$MeshRoad::showSpline"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Spline (Z)"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-spline"; + groupNum = "7"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(MeshRoadEditorWireframeBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "253 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$MeshRoad::wireframe"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Wireframe (X)"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-wireframe"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(MeshRoadEditorShowRoadBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiDefalutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "89 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$MeshRoad::showRoad"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Road Texture (V)"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-texture"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiControl(MeshRoadDefaultWidthTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "230 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "68 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Default Width"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "67 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.DefaultWidth = $ThisControl.getValue();"; + hovertime = "1000"; + text = "10"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "101 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(MeshRoadDefaultWidthSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes Default Road Width"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + new GuiControl(MeshRoadDefaultDepthTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "360 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "68 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Default Depth"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "67 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.DefaultDepth = $ThisControl.getValue();"; + hovertime = "1000"; + text = "10"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "101 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(MeshRoadDefaultDepthSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes Default Road Depth"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; +}; +new GuiMouseEventCtrl(MeshRoadDefaultWidthSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(MeshRoadDefaultWidthTextEditContainer.position) + firstWord(MeshRoadEditorToolbar.position) + 10 SPC + (getWord(MeshRoadDefaultWidthTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "MeshRoadDefaultWidthTextEditContainer-->textEdit.setValue( mFloatLength($ThisControl.getValue(), 2)); MeshRoadEditorGui.DefaultWidth = $ThisControl.getValue();"; + range = "0 100"; + ticks = "0"; + value = "10"; + }; +}; +new GuiMouseEventCtrl(MeshRoadDefaultDepthSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(MeshRoadDefaultDepthTextEditContainer.position) + firstWord(MeshRoadEditorToolbar.position) + 10 SPC + (getWord(MeshRoadDefaultDepthTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "MeshRoadDefaultDepthTextEditContainer-->textEdit.setValue( mFloatLength($ThisControl.getValue(), 2)); MeshRoadEditorGui.DefaultDepth = $ThisControl.getValue();"; + range = "0 100"; + ticks = "0"; + value = "10"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/images/DefaultHandle.png b/Templates/BaseGame/game/tools/missionAreaEditor/images/DefaultHandle.png new file mode 100644 index 0000000000000000000000000000000000000000..c32ed3fb8529f575734eb1d28b5b08eb2d874a53 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSpnVqirF9h-xl0AZa z85pWm85kOx85q8(Ffg>7XJE*UXJB}d#lT<#)FU0f@@=CSP_3P(i(^Q|oTMNB|JyU$ z@d!AqVB+BkP;8NKU`X^@q{+mg@Me-n7Xwqr0;UEAr3_XF2Eh^)2Zo1Vl{lDLBpABQ WmPa1?y?ZmzBnD4cKbLh*2~7Yznl3p2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_d.png b/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_d.png new file mode 100644 index 0000000000000000000000000000000000000000..356cf1f414f31a49ab1c91b1235da47c3da40af8 GIT binary patch literal 973 zcmV;;12X)HP)<h;3K|Lk000e1NJLTq0012T000{Z0ssI252zDG0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Z%IT!RCwB~R!vJAQ53yz=FLYU(YTB& zv9?RWt!P1TrTh8^;uk@J2w8}@P(iY&ir}Kg{s(`6(IPFl)b86XY(fRa;zY^kOwY_q z#z``hmeRX;yvaN7zH`sL@4m3y_ix#3)-Z}hWRb;)iIG~&vWUD(K1fDfmN{4YqkZEO z<1BOZp|>wOGBOg0L>Oyx_b-;u=YJOR+$a{uC*BSY4r-eAU;M^mv6GV<34ws=|8i>U z!I-M3+}6l3@$1~&oQue0GTmJSfmMa`jug@M_BO`#_4Ut;NkkG+U?B0h3$9cuQqwrB z3!OK&x3VmULZLudBH>OSA0HJ(>FMc#Uk2CEp$&|~#iVE$nyv$9Sys^5F4!;(<jFKm z@!3uUG9i30g;S7Kn&#{D@bD1fUq&LYbGdrG?m8t$y>`P-@Cc@%a2O)!x~>YlxKPK& z)V)1>zPzl?%&1RKSe1!@1RlE(T!U|C-mn69K!B$1=+Ic0nxdg0N~gg~Iy$maDYLiN zGEJ2z91d5jRTQfW_Th_jlF4K$l>#kf?8Psx@6L|BXKl?K9j$C`R?6jCtp>b^#|!2` z!j+vpZt1u+-|QDwsZ_#5;fik+N`X~oSzwLmT8d#|0YhzdRld4XH#V@u74GZTr_S8_ zNKOK=*%lO((`h<Ag%s6fvVM0LIyk6LPSW(W|MuP8_vk>D$1aH=La|uH2)@tH&w{mm z!2LZ63#Iz-@bD{_!wA?H4_?5|&$1GuZtZ4iX{kk<XjByG%F2o!i=n?by_h;L4*z3+ z5|=_qfxIE<a??Z_B~L#8B5$591=b5r#^&ee7Z(@d`uO-5pSWX^w+y}6+1uTHH#<8p zFwoihP;ID6R2%el?-Hnqs1X6qz^C|qe}5m50gw5o^D`eE+FBnuWe;EQE-C~xg+5r_ z+S<Y>z(OUlrJKHJD7gC?d^yQ6o43(wD#+q-R|b>~A^?MiUz{J*`p~7&P+%guPWzZh zxZrZBga}^xq{8L4L?I$HX%s2qtvVL;^g&aA!4yuJ)RIEZJGh8WCYrR}Fg2~qvw|Z5 z6IzS0;kUmpS|}9oIJ=~<hUY{?)Yf-=`hZPL^@8Eee%<pwZmz#x*Q#|qqIRuw*?BD+ vP0Ow-pT9rk0g(Hyiu%)ZZTQvjZvh4X<m;?-(xin-00000NkvXXu0mjfi&ES7 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_h.png b/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b96012a60889e0cae1fe49d9e85aa4f18549fdfe GIT binary patch literal 1218 zcmV;z1U>tSP)<h;3K|Lk000e1NJLTq0012T000{Z0ssI252zDG0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$WJyFpRCwB~Rm*N1MHH>7uI`?m$=DXM z6SE@O@>oO`t8DNFB5<UTMWj5wfru=yCB}Kp#7Pz)C9EP~g|mnyf(RS@0VqNdEV95X zZ6Se;AIQ_0NB7KBac)(0yFE_ChU%W0>Z;TC-a7ZxZN9U;6+e&FKrzNN)0}bJvqHlR z`r^_-q{X0l!mEx{WixJ8t0R?4h5cvK*?_-f)y%hBt+*MB!C+7>m&u(rF0;G%=*D?m z1=!xr+#o8KVN()JY3pk5ZPOG}2e&yie;{arAr7g?s=I*x=+dPebnounAx}kq(GP)h z4B55ji{2WI2Krj9_VtMqupv0-FJpXaDhDS?B243g@+lFnR*Jpo-I+(<dY%`CB`IaT zuCx50`(<`62RC<j{lG`6JVrL9p;z<5cH!I(;`n(G1lWT@Kb-bqaI+a#s)vFg<U$Z# zzpwl@Ir%DKvp2qZXZqyQQmfs95=#+XK^W|r)CH+&miE$z?|<;_;^IIJAlA{*(ZCPZ z*Z&gA5J8<LLNFjsh47^xf&yG&h%xT&%F628+&@1&_I-cj%dh^ZH$;+zL5Ku|Vc<(? zGQqX6MLJW&na&BcI6y$F9RP}jLvOw5ADP^I^qo>_dwF^M!_zD(ArznUQi*Z^(g3y* z3KypR(I2$Qu3f*eaO*ar6rP8ax+Sd-UpNo=#^QtU*s)j6p6zryX|D&D(li1iE~T7) zv%KZ0X4kJ>O%qU;;t_f9gcv%?c(c>#Sc}{^)-E<O0-G*grCh-1=|mCFs@}Mnrae1p z??3O~$LiS3OuD+d^Kc0>Bv^{kpt7Hd*BIs89S*BgyJjEz{XPOl$UEOZZasT8aqe8@ z<a^2PZfkkD^v3Jq@#7|Zmv_@MqhI&T^9?#i7@z-?wAt(Rq?TOru~Vn2)6*&p(=;8u zcyZ*+8D5UCAwvQyY}e$C^kCz+DzkQ(K{0is-pF!BHw2*60E63i?u27wo+qtTlpc@; zW;xYa-Jvy}1<D=28P`Y~u>Jl3RAL#AzRz*KkuLNw!HGoX)NkU&zqBq2wMA!q(S2pU z27aMAOwQM8byH<9dG*SC-!zg}0S}AZ5W$~5{dr=1!sLs}S>xtHe)eZ)Kf%$m@7n;G zDR0VcO)o6m!lW=NynOj%pBfd&&CN}lH(k)~UY$&n#72)pj6L<t5r6}H4y<xvv0ksE zH{cXtL0(8(Qadf1;P)5HAJ1PvbU3C6)u`Pn&q<Q1!PJz<j!di7LQ16sHmj2bZS5<4 zZ+~&GL@f#e-{o|_<6l4fJO`&~B7Juns7^k4^3yo@Ei~*mZZ_NPHr%2@mZAu>?f;5p z9<=WD``u0lD#k>)QmIy}C~RBXTN%H$xMnZ-K@b^cz$WEF$`=^TzWGW8lvR{g<KBJW z@X%?uqbPDk1-67h(WRni*hG~QoFDvBSo;!=rJo&oeA`5~(}hj`_s06#+8T}~`=1)V gaz|e~`mX>30G~`$T?ibl^#A|>07*qoM6N<$g7{Kd2><{9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_n.png b/Templates/BaseGame/game/tools/missionAreaEditor/images/mission-area_n.png new file mode 100644 index 0000000000000000000000000000000000000000..5612871e727f2d493b9e94529f9f8084e4824b65 GIT binary patch literal 845 zcmV-T1G4;yP)<h;3K|Lk000e1NJLTq0012T000{Z0ssI252zDG0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!?@2^KRCwBamOW2uK@f%o4j&g#Xsl@| ztPGf#;9mm6N3ID{L188T!_t<Dgu;YM_yraw7Bqel6omVD28VSH+$lD*d)|3>c4l_= zgr1+DL!r=~FRRrGUB*2}SYdQO1(AYL@f0MMI=ZCFrPHlQcN*5Y*-)qAmnep47wI>z zYi4jR_15*E=qJyFtXi!q==%CPXy$qy1nVbHHB~yD4&&wJWu;OH{m3o{75w@68U7KQ zHzS}Ukw`oq_dG9{cUCSgE)@Lw`WlT!lgZ?oH%JPdDDUs@u~=+Zc^L59+gmD?N+c5N zUA2DIfa5otO-R|9{ej}LTeEHn&FAyKzATqZcr%#{uB_`USWg@rE=d3Q67%Kh>FMU? z=KlVkPyK%X_VyM$yRtnaLD1lC@@A?X93xOM6KF1%8;wT8;SexNu~=j?+$0BhfaC0^ z;PBQ;B5Lh+8#5eNC~|go#yA)Z4i67cPEKaC*<!I^1rG$gsWZ38G{jd|S05iA*g(n( z9+_GMNntU<Xl~6Ge_aGNcn!6?*z5HekB^ThlgZuP9lC6(xzQ>NtG4pR6che_`GQVl zA08eGg~I9SX+EDHkH?RXkLFt0IGFB(4kOV(fmA+`sH3AJf=Gx;rIO^0bh+OA>N}<f zx{;wU-6<KT08m=3*1^F6!FA8-+Im~N+^-JpecwswB!_TCd|{nY3)J>WbL9=GbSb>9 z*XzJ)wHgYH-ENn4LL{4Q>Dn(ZFYX1!QbEL*2rhilyQD}?&}cLy6HKZ1s4udOXC5xM z^^qTJ_$2pAt^g|%V!1NVF-o752C(h~ZgVhkpbcU}5QW`+J@r}A+c=$0#e+39ad2za zeFDzU&uKcQpP7wSFPF=7iEK72q1f=Ma3U7zlbjbu>1VrMXzCYF8CglE)BF4T!s`Y9 z95|Y^bLmy`UhL}Y0+ZsCR%z=&p-W%nnz?<)z+}4o5lN|nf5pK(_`808{)5*;{1adR Xeyjx=)AJ4p00000NkvXXu0mjf(zl7R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/main.cs b/Templates/BaseGame/game/tools/missionAreaEditor/main.cs new file mode 100644 index 000000000..000197bc6 --- /dev/null +++ b/Templates/BaseGame/game/tools/missionAreaEditor/main.cs @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeMissionAreaEditor() +{ + echo(" % - Initializing Mission Area Editor"); + + exec( "./missionAreaEditor.ed.cs" ); + exec( "./missionAreaEditorGui.ed.gui" ); + exec( "./missionAreaEditorGui.ed.cs" ); + + // Add ourselves to EditorGui, where all the other tools reside + MissionAreaEditorGui.setVisible( false ); + MissionAreaEditorTerrainWindow.setVisible( false ); + MissionAreaEditorPropertiesWindow.setVisible( false ); + + EditorGui.add( MissionAreaEditorGui ); + EditorGui.add( MissionAreaEditorTerrainWindow ); + EditorGui.add( MissionAreaEditorPropertiesWindow ); + + new ScriptObject( MissionAreaEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = MissionAreaEditorGui; + }; + + MissionAreaEditorPlugin.initSettings(); +} + +function destroyMissionAreaEditor() +{ +} + +function MissionAreaEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Mission Area Editor", "", MissionAreaEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Mission Area Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "MissionAreaEditorPlugin", "MissionAreaEditorPalette", expandFilename("tools/missionAreaEditor/images/mission-area"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( MissionAreaEditorPropertiesWindow, MissionAreaEditorTerrainWindow); +} + +function MissionAreaEditorPlugin::onActivated( %this ) +{ + %this.readSettings(); + + EditorGui.bringToFront( MissionAreaEditorGui ); + + MissionAreaEditorGui.setVisible(true); + MissionAreaEditorGui.makeFirstResponder( true ); + + MissionAreaEditorTerrainWindow.setVisible( true ); + MissionAreaEditorPropertiesWindow.setVisible( true ); + + // Set the status bar here until all tool have been hooked up + EditorGuiStatusBar.setInfo("Mission Area Editor."); + EditorGuiStatusBar.setSelection(""); + + // Allow the Gui to setup. + MissionAreaEditorGui.onEditorActivated(); + + Parent::onActivated(%this); +} + +function MissionAreaEditorPlugin::onDeactivated( %this ) +{ + %this.writeSettings(); + + MissionAreaEditorGui.setVisible(false); + MissionAreaEditorTerrainWindow.setVisible( false ); + MissionAreaEditorPropertiesWindow.setVisible( false ); + + // Allow the Gui to cleanup. + MissionAreaEditorGui.onEditorDeactivated(); + + Parent::onDeactivated(%this); +} + +function MissionAreaEditorPlugin::setEditorFunction( %this ) +{ + %missionAreaExists = isObject(getMissionAreaServerObject()); + + if( %missionAreaExists == false ) + MessageBoxYesNoCancel("No Mission Area","Would you like to create a New Mission Area?", "MissionAreaEditorPlugin.createNewMissionArea();"); + + return %missionAreaExists; +} + +function MissionAreaEditorPlugin::createNewMissionArea(%this) +{ + %newMissionArea = new MissionArea(); + %newMissionArea.area = "-256 -256 512 512"; + + MissionGroup.add(%newMissionArea); + + EditorGui.setEditor(MissionAreaEditorPlugin); + + EWorldEditor.isDirty = true; +} +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function MissionAreaEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "MissionAreaEditor", true ); + + EditorSettings.setDefaultValue( "MissionBoundsColor", "255 255 255" ); + + EditorSettings.endGroup(); +} + +function MissionAreaEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "MissionAreaEditor", true ); + + MissionAreaEditorTerrainEditor.missionBoundsColor = EditorSettings.value("MissionBoundsColor"); + + EditorSettings.endGroup(); +} + +function MissionAreaEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "MissionAreaEditor", true ); + + EditorSettings.setValue( "MissionBoundsColor", MissionAreaEditorTerrainEditor.missionBoundsColor ); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditor.ed.cs b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditor.ed.cs new file mode 100644 index 000000000..dfdeaf970 --- /dev/null +++ b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditor.ed.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile( MissionAreaEditorProfile ) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192"; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.cs b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.cs new file mode 100644 index 000000000..7d8e79225 --- /dev/null +++ b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.cs @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function MissionAreaEditorGui::onEditorActivated( %this ) +{ + EWorldEditor.clearSelection(); + + %ma = getMissionAreaServerObject(); + if( isObject( %ma ) ) + { + EWorldEditor.selectObject( %ma ); + EWorldEditor.syncGui(); + MissionAreaEditorTerrainEditor.updateTerrain(); + %this.setSelectedMissionArea( %ma ); + %this.onMissionAreaSelected( %this.getSelectedMissionArea() ); + } +} + +function MissionAreaEditorGui::onEditorDeactivated( %this ) +{ +} + +function MissionAreaEditorGui::onMissionAreaSelected( %this, %missionArea ) +{ + %this.missionArea = %missionArea; + MissionAreaEditorTerrainEditor.setMissionArea( %missionArea ); + MissionAreaInspector.inspect( %missionArea ); +} + +//----------------------------------------------------------------------------- + +function MissionAreaEditorTerrainEditor::onMissionAreaModified( %this ) +{ + MissionAreaInspector.refresh(); +} + +function MissionAreaEditorTerrainEditor::onUndo( %this ) +{ + MissionAreaInspector.refresh(); +} + +//----------------------------------------------------------------------------- + +function MissionAreaInspector::inspect( %this, %obj ) +{ + %name = ""; + if ( isObject( %obj ) ) + %name = %obj.getName(); + else + MissionAreaFieldInfoControl.setText( "" ); + + //RiverInspectorNameEdit.setValue( %name ); + Parent::inspect( %this, %obj ); +} + +function MissionAreaInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); +} + +function MissionAreaInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + MissionAreaFieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} diff --git a/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui new file mode 100644 index 000000000..03e8fdfe5 --- /dev/null +++ b/Templates/BaseGame/game/tools/missionAreaEditor/missionAreaEditorGui.ed.gui @@ -0,0 +1,245 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiMissionAreaEditorCtrl(MissionAreaEditorGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "MissionAreaEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + + new GuiWindowCollapseCtrl(MissionAreaEditorTerrainWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 230"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Mission Area"; + + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "5 25"; + Extent = "200 200"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiMissionAreaCtrl(MissionAreaEditorTerrainEditor) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "EditorDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "200 200"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + wrap = "0"; + enableMirroring = "0"; + mirrorIndex = "0"; + mirrorLineColor = "255 0 255 255"; + mirrorArrowColor = "255 0 255 128"; + handleFrameColor = "255 255 255 255"; + handleFillColor = "0 0 0 255"; + defaultObjectColor = "0 255 0 100"; + waterObjectColor = "0 0 255 100"; + missionBoundsColor = "255 0 0 255"; + cameraColor = "255 0 0 255"; + squareBitmap = "1"; + enableEditing = "0"; + renderCamera = "1"; + handleBitmap = "tools/missionAreaEditor/images/DefaultHandle.png"; + }; + }; + }; + new GuiWindowCollapseCtrl(MissionAreaEditorPropertiesWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(MissionAreaEditorTerrainWindow.extent, 1) - 2; + Extent = "210 466"; + MinExtent = "210 300"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(){ //Mission Area Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 112"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "Mission Area Properties"; + }; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 129"; + Extent = "202 357"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "-14 0 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(MissionAreaInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + name = "MissionAreaInspector"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "1 1"; + Extent = "178 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + }; + }; + new GuiContainer(){ //Mission Area Properties + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "202 42"; + Docking = "Bottom"; + Margin = "0 0 3 3"; + + new GuiMLTextCtrl(MissionAreaFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 0"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + }; +}; diff --git a/Templates/BaseGame/game/tools/navEditor/CreateNewNavMeshDlg.gui b/Templates/BaseGame/game/tools/navEditor/CreateNewNavMeshDlg.gui new file mode 100644 index 000000000..755bce30a --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/CreateNewNavMeshDlg.gui @@ -0,0 +1,392 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(CreateNewNavMeshDlg) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl() { + text = "New NavMesh"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "0"; + closeCommand = "Canvas.popDialog(CreateNewNavMeshDlg);"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "283 240"; + extent = "200 176"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Name:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 29"; + extent = "39 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "Nav"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "59 30"; + extent = "129 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "MeshName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Position:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 51"; + extent = "39 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "0 0 0"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "59 52"; + extent = "129 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "MeshPosition"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Scale:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "12 73"; + extent = "39 21"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "50 50 20"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "59 74"; + extent = "129 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "MeshScale"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(MeshMissionBounds) { + text = " Fit NavMesh to mission area"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "1"; + position = "22 99"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Positions and scales the NavMesh so it includes all your mission objects."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl(MeshTerrainBounds) { + text = " Include terrain"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "22 121"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Consider terrain when calculating NavMesh bounds."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Create!"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "12 146"; + extent = "87 19"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "CreateNewNavMeshDlg.create();"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "104 146"; + extent = "84 19"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "Canvas.popDialog(CreateNewNavMeshDlg);"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function CreateNewNavMeshDlg::onWake(%this) +{ + %this-->MeshName.setText("Nav"); + %this-->MeshPosition.setText("0 0 0"); + %this-->MeshScale.setText("50 50 20"); + MeshMissionBounds.setStateOn(false); + MeshTerrainBounds.setStateOn(true); +} + +function MissionBoundsExtents(%group) +{ + %box = "0 0 0 0 0 0"; + foreach(%obj in %group) + { + %cls = %obj.getClassName(); + if(%cls $= "SimGroup" || %cls $= "SimSet" || %cls $= "Path") + { + // Need to recursively check grouped objects. + %wbox = MissionBoundsExtents(%obj); + } + else + { + // Skip objects that are too big and shouldn't really be considered + // part of the scene, or are global bounds and we therefore can't get + // any sensible information out of them. + if(%cls $= "LevelInfo") + continue; + if(!MeshTerrainBounds.isStateOn() && %cls $= "TerrainBlock") + continue; + + if(!(%obj.getType() & $TypeMasks::StaticObjectType) || + %obj.getType() & $TypeMasks::EnvironmentObjectType) + continue; + + if(%obj.isGlobalBounds()) + continue; + + %wbox = %obj.getWorldBox(); + } + + // Update min point. + for(%j = 0; %j < 3; %j++) + { + if(GetWord(%box, %j) > GetWord(%wbox, %j)) + %box = SetWord(%box, %j, GetWord(%wbox, %j)); + } + // Update max point. + for(%j = 3; %j < 6; %j++) + { + if(GetWord(%box, %j) < GetWord(%wbox, %j)) + %box = SetWord(%box, %j, GetWord(%wbox, %j)); + } + } + return %box; +} + +function CreateNewNavMeshDlg::create(%this) +{ + %name = %this-->MeshName.getText(); + if(%name $= "" || nameToID(%name) != -1) + { + MessageBoxOk("Error", "A NavMesh must have a unique name!"); + return; + } + + %mesh = 0; + + if(MeshMissionBounds.isStateOn()) + { + if(!isObject(MissionGroup)) + { + MessageBoxOk("Error", "You must have a MissionGroup to use the mission bounds function."); + return; + } + // Get maximum extents of all objects. + %box = MissionBoundsExtents(MissionGroup); + %pos = GetBoxCenter(%box); + %scale = (GetWord(%box, 3) - GetWord(%box, 0)) / 2 + 5 + SPC (GetWord(%box, 4) - GetWord(%box, 1)) / 2 + 5 + SPC (GetWord(%box, 5) - GetWord(%box, 2)) / 2 + 5; + + %mesh = new NavMesh(%name) + { + position = %pos; + scale = %scale; + }; + } + else + { + %mesh = new NavMesh(%name) + { + position = %this-->MeshPosition.getText(); + scale = %this-->MeshScale.getText(); + }; + } + MissionGroup.add(%mesh); + NavEditorGui.selectObject(%mesh); + + Canvas.popDialog(CreateNewNavMeshDlg); +} + +function MeshMissionBounds::onClick(%this) +{ + MeshTerrainBounds.setActive(%this.isStateOn()); +} diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorConsoleDlg.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorConsoleDlg.gui new file mode 100644 index 000000000..3f59069a3 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorConsoleDlg.gui @@ -0,0 +1,169 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCtrl(NavEditorConsoleDlg) { + text = "Nav Console"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "1"; + canMaximize = "1"; + canCollapse = "0"; + closeCommand = "NavEditorConsoleDlg.setVisible(false);"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "238 170"; + extent = "320 240"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiTextCtrl() { + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "3 222"; + extent = "149 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "top"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "StatusLeft"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "-14 41 3 3"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "3 23"; + extent = "314 194"; + minExtent = "8 2"; + horizSizing = "relative"; + vertSizing = "relative"; + profile = "GuiEditorScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "OutputScroll"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiListBoxCtrl() { + allowMultipleSelections = "0"; + fitParentWidth = "1"; + colorBullet = "1"; + position = "1 1"; + extent = "312 16"; + minExtent = "8 2"; + horizSizing = "relative"; + vertSizing = "relative"; + profile = "GuiListBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "Output"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +new ScriptMsgListener(NavEditorConsoleListener); +getNavMeshEventManager().subscribe(NavEditorConsoleListener, "NavMeshCreated"); +getNavMeshEventManager().subscribe(NavEditorConsoleListener, "NavMeshRemoved"); +getNavMeshEventManager().subscribe(NavEditorConsoleListener, "NavMeshStartUpdate"); +getNavMeshEventManager().subscribe(NavEditorConsoleListener, "NavMeshUpdate"); +getNavMeshEventManager().subscribe(NavEditorConsoleListener, "NavMeshTileUpdate"); + +function NavEditorConsoleListener::onNavMeshCreated(%this, %data) +{ +} + +function NavEditorConsoleListener::onNavMeshRemoved(%this, %data) +{ +} + +function NavEditorConsoleListener::onNavMeshStartUpdate(%this, %data) +{ + NavEditorConsoleDlg-->Output.clearItems(); + NavEditorConsoleDlg-->Output.addItem("Build starting for NavMesh" SPC %data, "0 0.6 0"); + NavEditorConsoleDlg-->OutputScroll.scrollToBottom(); +} + +function NavEditorConsoleListener::onNavMeshUpdate(%this, %data) +{ + %message = ""; + if(getWordCount(%data) == 2) + { + %seconds = getWord(%data, 1); + %minutes = mFloor(%seconds / 60); + %seconds -= %minutes * 60; + %message = "Built NavMesh" SPC getWord(%data, 0) SPC "in" SPC %minutes @ "m" SPC mRound(%seconds) @ "s"; + if(NavEditorGui.playSoundWhenDone) + { + sfxPlayOnce(Audio2D, "tools/navEditor/done.wav"); + } + } + else + { + %message = "Loaded NavMesh" SPC %data; + } + NavEditorConsoleDlg-->Output.addItem(%message, "0 0.6 0"); + NavEditorConsoleDlg-->OutputScroll.scrollToBottom(); + NavEditorConsoleDlg->StatusLeft.setText(""); +} + +function NavEditorConsoleListener::onNavMeshTileUpdate(%this, %data) +{ + %mesh = getWord(%data, 0); + %index = getWord(%data, 1); + %total = getWord(%data, 2); + %tile = getWords(%data, 3, 4); + %success = getWord(%data, 5) == "1"; + if(!%success) + { + %message = "NavMesh" SPC %mesh SPC "tile" SPC %tile SPC "build failed!"; + NavEditorConsoleDlg-->Output.addItem(%message, "1 0 0"); + NavEditorConsoleDlg-->OutputScroll.scrollToBottom(); + } + %percent = %index / %total * 100; + NavEditorConsoleDlg->StatusLeft.setText("Build progress:" SPC mRound(%percent) @ "%"); +} diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui new file mode 100644 index 000000000..de78d5ee7 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -0,0 +1,854 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "NavEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + + new GuiWindowCollapseCtrl(NavEditorTreeWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 167"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Navigation"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "115 2"; + extent = "90 18"; + text = "New NavMesh"; + command = "Canvas.pushDialog(CreateNewNavMeshDlg);"; + }; + + new GuiContainer(){ + profile = GuiDefaultProfile; + Position = "5 25"; + Extent = "200 120"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 118"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(NavTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + }; + }; + new GuiWindowCollapseCtrl(NavEditorOptionsWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(NavEditorTreeWindow.extent, 1) - 2; + Extent = "210 530"; + MinExtent = "210 300"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(){ //Actions + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "202 85"; + Docking = "Top"; + Margin = "3 3 3 3"; + internalName = "ActionsBox"; + + new GuiTextCtrl(){ + Profile = "GuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "86 18"; + text = "Actions"; + }; + new GuiStackControl() + { + internalName = "SelectActions"; + position = "7 21"; + extent = "190 64"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "182 18"; + text = "Build NavMesh"; + command = "NavEditorGui.buildSelectedMeshes();"; + }; + new GuiControl() { + profile = "GuiDefaultProfile"; + Extent = "182 20"; + position = "0 20"; + + new GuiCheckboxCtrl() { + internalName = "BackgroundBuildButton"; + text = "Background"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "75 20"; + minExtent = "8 2"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "NavEditorGui.backgroundBuild"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckboxCtrl() { + position = "75 0"; + internalName = "SaveIntermediatesButton"; + text = "Keep intermediates"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "105 20"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "NavEditorGui.saveIntermediates"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiCheckboxCtrl() { + internalName = "BuildSoundButton"; + text = "Play sound when done"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "150 20"; + minExtent = "8 2"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "NavEditorGui.playSoundWhenDone"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiStackControl() + { + internalName = "LinkActions"; + position = "7 21"; + extent = "190 64"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "182 18"; + text = "Rebuild links"; + command = "NavEditorGui.buildLinks();"; + }; + }; + new GuiStackControl() + { + internalName = "CoverActions"; + position = "7 21"; + extent = "190 64"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "182 18"; + text = "Create Cover"; + command = "NavEditorGui.createCoverPoints();"; + }; + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "182 18"; + text = "Delete Cover"; + command = "NavEditorGui.deleteCoverPoints();"; + }; + }; + new GuiStackControl() + { + internalName = "TileActions"; + position = "7 21"; + extent = "190 64"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "182 18"; + text = "Rebuild tile"; + command = "NavEditorGui.buildTile();"; + }; + }; + new GuiStackControl() + { + internalName = "TestActions"; + position = "7 21"; + extent = "190 64"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "180 18"; + text = "Spawn"; + command = "NavEditorGui.spawnPlayer();"; + }; + new GuiControl() { + profile = "GuiDefaultProfile"; + Extent = "190 18"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "90 18"; + text = "Delete"; + command = "NavEditorGui.getPlayer().delete();"; + }; + new GuiButtonCtrl() { + position = "100 0"; + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "90 18"; + text = "Find cover"; + command = "NavEditorGui.findCover();"; + }; + }; + new GuiControl() { + profile = "GuiDefaultProfile"; + Extent = "190 18"; + + new GuiButtonCtrl() { + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "90 18"; + text = "Follow"; + command = "NavEditorGui.followObject();"; + }; + new GuiButtonCtrl() { + position = "100 0"; + Profile = "GuiButtonProfile"; + buttonType = "PushButton"; + HorizSizing = "right"; + VertSizing = "bottom"; + Extent = "90 18"; + text = "Stop"; + command = "NavEditorGui.getPlayer().stop();"; + }; + }; + }; + }; + new GuiContainer(){ + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 112"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "GuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "Properties"; + }; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 129"; + Extent = "202 357"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "-14 41 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + internalName = "PropertiesBox"; + + new GuiInspector(NavInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + name = "NavInspector"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "1 1"; + Extent = "178 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + }; + + new GuiStackControl() { + internalName = "LinkProperties"; + position = "7 21"; + extent = "186 64"; + padding = "2 2 2 2"; + + new GuiCheckBoxCtrl() { + internalName = "LinkWalkFlag"; + class = "NavMeshLinkFlagButton"; + text = " Walk"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "This link is just ordinary flat ground."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkJumpFlag"; + class = "NavMeshLinkFlagButton"; + text = " Jump"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Does this link require a jump?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkDropFlag"; + class = "NavMeshLinkFlagButton"; + text = " Drop"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Does this link involve a significant drop?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkLedgeFlag"; + class = "NavMeshLinkFlagButton"; + text = " Ledge"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Should the character jump at the next ledge?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkClimbFlag"; + class = "NavMeshLinkFlagButton"; + text = " Climb"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Does this link involve climbing?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkTeleportFlag"; + class = "NavMeshLinkFlagButton"; + text = " Teleport"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Is this link a teleporter?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiStackControl() { + internalName = "TileProperties"; + position = "7 21"; + extent = "186 64"; + padding = "2 2 2 2"; + + new GuiCheckBoxCtrl() { + text = " Display input geometry"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + variable = "$Nav::Editor::renderInput"; + }; + new GuiCheckBoxCtrl() { + text = " Display voxels"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + variable = "$Nav::Editor::renderVoxels"; + }; + }; + new GuiStackControl() { + internalName = "TestProperties"; + position = "7 21"; + extent = "186 64"; + padding = "2 2 2 2"; + + new GuiTextCtrl() { + text = "Cover"; + profile = "GuiTextProfile"; + extent = "180 20"; + minExtent = "8 2"; + visible = "1"; + }; + new GuiTextEditCtrl() { + internalName = "CoverRadius"; + text = "10"; + profile = "GuiTextEditProfile"; + extent = "40 20"; + minExtent = "8 2"; + visible = "1"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Radius for cover-finding."; + }; + new GuiTextEditCtrl() { + internalName = "CoverPosition"; + text = "LocalClientConnection.getControlObject().getPosition();"; + profile = "GuiTextEditProfile"; + extent = "140 20"; + minExtent = "8 2"; + visible = "1"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Position to find cover from."; + }; + new GuiTextCtrl() { + text = "Follow"; + profile = "GuiTextProfile"; + extent = "180 20"; + minExtent = "8 2"; + visible = "1"; + }; + new GuiTextEditCtrl() { + internalName = "FollowRadius"; + text = "1"; + profile = "GuiTextEditProfile"; + extent = "40 20"; + minExtent = "8 2"; + visible = "1"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Radius for following."; + }; + new GuiTextEditCtrl() { + internalName = "FollowObject"; + text = "LocalClientConnection.player"; + profile = "GuiTextEditProfile"; + extent = "140 20"; + minExtent = "8 2"; + visible = "1"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Object to follow."; + }; + new GuiTextCtrl() { + text = "Movement"; + profile = "GuiTextProfile"; + extent = "180 20"; + minExtent = "8 2"; + visible = "1"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkWalkFlag"; + class = "NavMeshTestFlagButton"; + text = " Walk"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character walk on ground?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkJumpFlag"; + class = "NavMeshTestFlagButton"; + text = " Jump"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character jump?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkDropFlag"; + class = "NavMeshTestFlagButton"; + text = " Drop"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character drop over edges?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkLedgeFlag"; + class = "NavMeshTestFlagButton"; + text = " Ledge"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character jump from ledges?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkClimbFlag"; + class = "NavMeshTestFlagButton"; + text = " Climb"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character climb?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + internalName = "LinkTeleportFlag"; + class = "NavMeshTestFlagButton"; + text = " Teleport"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + extent = "159 15"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "0"; + tooltipProfile = "GuiToolTipProfile"; + toolTip = "Can this character teleport?"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiMLTextCtrl(NavFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 485"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "GuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + +}; + +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorSettingsTab.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorSettingsTab.gui new file mode 100644 index 000000000..8611ca3d1 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorSettingsTab.gui @@ -0,0 +1,506 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiTabPageCtrl(ENavEditorSettingsPage) { + fitBook = "1"; + text = "Navigation Editor"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "208 292"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiSolidDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "208 292"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + dynamicNonStackExtent = "0"; + dynamicPos = "0"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "206 124"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiRolloutCtrl() { + caption = "Test spawn"; + margin = "0 3 0 0"; + defaultHeight = "40"; + expanded = "1"; + clickCollapse = "1"; + hideHeader = "0"; + autoCollapseSiblings = "0"; + position = "0 0"; + extent = "206 62"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiRolloutProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "3"; + dynamicSize = "1"; + dynamicNonStackExtent = "0"; + dynamicPos = "0"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "0 20"; + extent = "206 39"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "206 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Spawn class:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 1"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrlEx() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + hotTrackCallback = "0"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "81 0"; + extent = "121 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "SpawnClassOptions"; + class = "ESettingsWindowPopup"; + canSave = "1"; + canSaveDynamicFields = "1"; + editorSettingsRead = "NavEditorPlugin.readSettings();"; + editorSettingsValue = "NavEditor/SpawnClass"; + editorSettingsWrite = "NavEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + position = "0 21"; + extent = "206 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Datablock:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 1"; + extent = "70 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "DefaultPlayerData"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "81 0"; + extent = "121 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + class = "ESettingsWindowTextEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + editorSettingsRead = "NavEditorPlugin.readSettings();"; + editorSettingsValue = "NavEditor/SpawnDatablock"; + editorSettingsWrite = "NavEditorPlugin.writeSettings();"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + caption = "Colors"; + margin = "0 3 0 0"; + defaultHeight = "40"; + expanded = "1"; + clickCollapse = "1"; + hideHeader = "0"; + autoCollapseSiblings = "0"; + position = "0 62"; + extent = "206 62"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiRolloutProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "3"; + dynamicSize = "1"; + dynamicNonStackExtent = "0"; + dynamicPos = "0"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "0 20"; + extent = "206 39"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "206 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "ESettingsWindowColor"; + canSave = "1"; + canSaveDynamicFields = "1"; + editorSettingsRead = "NavEditorPlugin.readSettings();"; + editorSettingsValue = "NavEditor/HoverSplineColor"; + editorSettingsWrite = "NavEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Hover Spline:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorEdit"; + class = "ESettingsWindowColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiSwatchButtonCtrl() { + color = "0 0 0 0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorButton"; + class = "ESettingsWindowColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + new GuiControl() { + position = "0 21"; + extent = "206 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + class = "ESettingsWindowColor"; + canSave = "1"; + canSaveDynamicFields = "1"; + editorSettingsRead = "NavEditorPlugin.readSettings();"; + editorSettingsValue = "NavEditor/SelectedSplineColor"; + editorSettingsWrite = "NavEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sel. Spline:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorEdit"; + class = "ESettingsWindowColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiSwatchButtonCtrl() { + color = "0 0 0 0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "ColorButton"; + class = "ESettingsWindowColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui new file mode 100644 index 000000000..832324475 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui @@ -0,0 +1,144 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(NavEditorToolbar,EditorGuiGroup) { + position = "306 0"; + extent = "800 32"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "NavEditorToolbar"; + canSave = "1"; + canSaveDynamicFields = "1"; + enabled = "1"; + + new GuiTextCtrl() { + text = "Navigation Editor"; + maxLength = "255"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 6"; + extent = "150 20"; + minExtent = "8 8"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "core/art/gui/images/separator-h.png"; + wrap = "0"; + position = "90 3"; + extent = "2 26"; + minExtent = "1 1"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl(NavEditorAboutBtn) { + text = "Console"; + groupNum = "7"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "100 6"; + extent = "54 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiButtonProfile"; + visible = "1"; + active = "1"; + command = "NavEditorConsoleDlg.setVisible(!NavEditorConsoleDlg.isVisible());"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Show Console"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Mesh"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "167 1"; + extent = "50 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$Nav::Editor::renderMesh"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "MeshButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "Portals"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "224 1"; + extent = "54 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$Nav::Editor::renderPortals"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "PortalButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + text = "BV tree"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "286 1"; + extent = "140 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + variable = "$Nav::Editor::renderBVTree"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "BVTreeButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/navEditor/done.wav b/Templates/BaseGame/game/tools/navEditor/done.wav new file mode 100644 index 0000000000000000000000000000000000000000..826250ddd7498b5afba6f79e8c98545772f965bb GIT binary patch literal 36948 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�V75N9_2LsII zam+a8J@P&BZRBm_m$)x+Howh3-8$WROLI$uRbUl!<a6XPsu<Nn!$ZSt&ukB`i`R8N z^L*wx{5kx0+ILz3tAIrT*g)Ixf32hu(g-=+ak%5B>!%Amb6_1eTRvO9Pq9xiRXbI? z%eKqbBhVwTF>zy}5G6!0C=3c%W8GohVS#$a3DOCY5AK6EL^VWBt({sMV~Mf+R{vIq zDZ&(Y<agx2^-;AM+l;HLR#(00_om;OtTS162zLlu=v(OE%*enrFwarXQ7Kpoc46AW zw6K=27O<x5ukWv)r<|u;FJCVYSB5L6>!$0V4yYsNea`!NDf3d&Flm@iluwjh%w5ct ztd%S~-A)I6B4E$7b;#BsiPeeKZKgKU3iS$go4if_9{^Z`@ytB)qv}W1eIoiq6lND@ zUn5>4Hqo2t3s?(S;2pY8xleh5euBQ5d^LGv<HkmZ-C<|w7`iBBl=6c7g8ZiP=Ku3k zy|dn#(wfrRme!WG2)hV-o_e18kok}aYM&|e6grB4B6L7IpkQo?u8OYm7(K=oRg20m z_sa_u1&R%t4VrtFdzRF?)ViAJn&?PGBw`<FA890GBx4+F9LvhEGQip}8WoM&p0GWk zsG+D~m2H)6hjxb+XnPd~xj{ZsHBwb<C^iV)LU;Jb@Q>sSaz+WR1c#+zX=<jL=>V8c zn@$5g1Huep#`(_koh%Q_<1_dSTU1+AL3vPaQ`i)9wR5#l8`Ng3H`ZT>zYu>Bc@a5{ zJdF&_9FZ&}iw~F_uaT~i<{{=GY%#VNSDmXa)0%0uX>6K%ihGKo0N+*LRZ^qW_|E&z z3(kQ7ix>0({sI0O?HLVdYnU4V_E7gw!Fj^m^ttKzZTW3STt{48`YwH(DozFFkIBl( z%Bi}kx;OSWb|Ab2pC>&}3PXpX0gDjD#c(mcGru#h(XY{W6L%Afvx~EHBXcA7*6gk6 zG<TYr8m8u;;-F$4K!v(OJ-|G`T;r?poeMt~4uwHs4+#$ma5|h0{w#d~u2Qa2!Z2YN zKtf4b(6pfGyW_h9D9QC;E{svcDC(8<%DMWv`a)-+b3*fk=7p&XQ>S94Vq_E<r9ZPj z^BurP+DF<%!bHNwtczJ225uO*)VI{<GPz9U>T>l)#YRO1K(VG+v(U28f~&>V77Z&J zR-Ij)eU5mJc#M9G4!)y9j6;kE<Ok%fsI4e>qB{{8L<Yf`#BAMc-EQS>Wv(Ju@we)4 z)kecc1I~?epK3qVu1r^^7h{XD^QiNvC?*Q*(wIaVkrsiE`2YOjpU?k%-sRopwHmEP z;J;9V^E0>-u3WBNuC-Zh*4O^m{>!nKW7i?pAy$)ClN#s^^ipOi^C$f${T}Ha$$@Yn z_QdUpE37Z9r`RaAhuVkQvC6T^or;|bq#CKdZoF=c^~QR4bnWPB&1lVdfO~*zp|((Y zOdg=v$1n@2h17T0cUS>L0IBJy=@7fc?s`ML!K$*Vz|R2n;#s;`x|#Nw_QQe0fzJt_ z6Pl1sND7%kMlcYJ-OSz0h4h7Vum*{N$H4bQ?TPwa`?>bJ<+~+Z8?J>ZVM>)krD#w$ zsC_1%3F3qJ{^|EmziMbT^cnsc9-LQ!G3pWH5#uQ3C}lEcGDe%KO<mWrt_A9Xy21=$ z|3Cld6z3Fh6<jq_KU4q3@x@W`zT&+p*_13m3((EvX7YT-e8y<zXeMAmp{*vYCV)K( zX$WbEqFPZ6j6uvhG&?jyl|z*&01q_}HAoB6GOcD>&5NNghTewVhTSFHC4h5PP}ig} zXp9@=8)UGqk4%nC?r!XEyzjX0;OIGeyb7<XQ~;<dRf`Ra4Ub%pT>D!0wSwBxZp>~B z_&Xe79AWff_F?v?^`|lL3_Kl5|9@=JR%xsBCcO!axqX#=m3shoYj<mlt;N=Nb?@pn zMQ@580UrTBKs-Q1)6sM|6V7~2e@_3G^e+j_Ex+P_#rF>M4vevnu^-SK(0x{ZR(?`^ zQh+t?0pkJVF3&E{?2ofQ9)TQz+{50(rchI;U|xI*Fq}G^+KKJNMrK52)OFT%{_y<p zIE)S>IJ+LG90)k(x|NG{i*>#2z3uZF<~5|or^c%gD#RqxB+_R3W;*!$WYRO~VBS9k zKLsz0DU4a?U+3><>u2+8{aTGu15{6BWuzuj^UCzfR8&<|1<o)}WS+<bEUdVz)T>nR z=a>Mnj<Sw&26F~ekyerRwC!ozWcOtEC&MQLNlj9N?`OSgy=snrjvlOAr#4P)L?j`S zkSHW7oE%PmNq<SVF>H)|GyuXr0+5bjfaNg)S&OW#wp9PW|0z|LD)#{Za+T(r`J4H8 z_3`Q%;WNS)Wi84o$Cu-W(uUH?7-fuldOiIV`4l+|orOlHpi?|eo+g9S;LI>&7(ibQ zAX_PWs(Y#n3<U<7i{=6>-_1Z<nX(SO4*i1sf*ha+=>IVOVYsL+>P-AhJb0#g19<~M zZ2{EE)Ec$svhp%8z4a@vYOiYZt@&0*t)n(!Si&%143&MJaGtP+wuW|{ah*{}FQnff z-5^~<UPDeuoRIi5_%yi40ia)`zoojRTCQBKysW;gE;1GwL++5fqrIaYk`76~fw_Sh zOBqW6dy!E9$<$<O3N8g_&M;@FKdV1;syJ0Tlg>0nGevVrc}Y1|HCDAvw@vrj_S!b7 zeo{R$78!dTejUD?xSS}X$!L2Rdl){Nj|Tc>dm?%w2E`4E`%(X+KHeU02Yu;lRclrE zl=qZ*nmo-#(?yfZ>+)81Rd#`z+iUD=ED(Yyaf~=d2fc$ni86^|#27K>)6b_<K2Sa! z^c?g660h;2`lA~B3?MA13iXA0nuF$u3C0B1Cag^uf*gWGkx-;CU=BB(F`bb|%cGqp zoF@E{{YQ3cRBF`uy7P5GYtWjeOVf>0jZ=M8epLR@{Lom;7PH&u_O0x<vL6HrfxgAP z#f_$prY19z83*VG=v&EK$;;8p(U??BYF$fR%V*bT7w99psJ^I{C?!hJD>L6P-vGXU zpu}hbYvp;Uc_=T*OJdPk^fX2q;~MoEbsc^kenZxVtk%J;gNtg4YP1%u<&5@>7W6`n z002f3+C*!jwO4Jg+J!?G4z0<m$-0NXhkr_aN{wPfF^18H(Mw1rq%LF^a(B}1q@wpl z@Ao?QI!78t8m6eHsKL6wMqQ&`Zd`8M?%wWhZ*6aVoccKREcz^ZGkG(40eu181JFik zqwsNj+>Xp0nPA<r-?!f<H_OdQ+9WL)qaFa9(Vfw4vTd@>_0RPWjvgGnF?(b7Zo+QD zK-xgs5Bd-K3EBzT72*}*BE%xZ_W14bC4rIv#({C1(Vx*LtCQ8Ks#Mi3%`VLW(*e^c z?<jBS$I_40>DB2kF)uNFDSath>05zs_5dA4LH%E+nh%)|L3~1dl6WOvyUA{Ps(GrR zsc0(jvo`BD>)+eo+q)aO8*aqihy!)Wk;IY2$+XEd7u`kwP5n&;`y0Swnhkm!BkLpU zJ8hk|&$`dLE>#!Mb<S7ev^Z_Kx!er)A>jPYnc>V3VMW;8l-(3i(}DraA<rS-L*GNk zrUA6awgWAk=d1Cn5$wIksK%(k_r27x)Ue9A%30M|)p$Aaa^fiDDC7^~58^S}G1?va z9r^<50_q<89{i82A6aK2&O|tCowY!Ewl2~w(z#V`)l+~rZJYLk<%1=+CbtGC{ljlU zZ$h8pp5cIFPT5W0O@B>$O#|mP925stl3bEJsd-ZK9oHQfa2<>TGy^m%R4Y_44NSAl zxXd`%J=hJT<QCxCrW`^YLR}|aC%vV;rGYWw73CFWHf}a937P~QFmS*?TeYoPVv$$? zsb8yAX;nkiL)5QyuXH>c&$hE}XWg5~H<2S?BVZ-?5<KXATti<&=g>H`Q^Zq5Awq}% zq>%)6BfBxrnddAr6dAyt^OEY4N~{rUwwkt@z&>Yu$M}v-X`9l(+8VI>lif5oErFgu z2Ys?oEEF4`5ufqv^RLf6d_8<xv({XsE&Bh@vRS=ZeNul?4{C)U>Oa&!k9i)W$<|~S z5DEyFsF$dd=#%K+EaVK~3?Uny4L=!sGWJZvnFdflGU|=`E9xt1m&&DDtX-_VWWHp+ zTy?qXSJ$tueUN<+F-D95V{Q|zi6#P|k!fTv+KX;YYfU@;;rNFs-YMQjQ==(b8?6Q3 z5g22(7`7M^or%ud!P~)>_?CDq0*go@CJ}v99~C?+snk^J5&RMSCfFv}>tU~l@%?<i z)8@1>^bGxW^>%eSK(a1bXRsJ7|5X1|o!2j~UqNO;CRi^4_7X}ft(7(dU_NO+X(wtY zsyL-MWn$~ZR-Ietjy1)aWEz>KTh*;9(iCad7}pr@yY9PQHN9%Oopd{C6LJ$$PLvae z(1y_30k%=LQC8tr;T}RCLaz_LJ{VDpsFheH)@QnBx+Ur*>P-N(x>{YNEz<U^_F3() zp#WLKvTovT;)*H7lsZ}+EkF%WcM^9J!P$3vVteAZ_ut+xa4m4nFwQW7_ZYl~{j~kG z8%-Nc;9PoU+sw8BsRL5)qVA&RlID`Yy7V4^maHY`U~{lw9|Fdf;nl;d0m;Y0)p2!y zss94kqts9IPxOoJi|xJYd)42F0)X9s72%8Ub(A`anx>}xMg5C<lyH==AHE+RiVMYo zeuo%mjI-2GY8ay#qnV_hq`s%Ur#)sqW(Iq%-yeT}M5UwB0Xru;ND7ih(MADE?Gzd~ z%kP8fgDFifP2cco!>6NFN2`E!AhS#B(k@poSA(Ag`kMji(@`8K4nX1{aYNxl;Zy>Z z0HeaFH)%I%c8Z+>zGpz1hwYEvAN{=kc|EAJMi?RtPz_XbMtw#-MK?wF-16M=#rMSr z&W^w{4xYI`$$ye3&?eA8ZH!Cel0e;bW$Mb*9ql{Xzj(fQdYOBfcWD6tT|xsqJB&Mw z6I~Nsi{3AKKQM7%;(TD9*hpw3{7L<jx|+6{2KMBeahq{K$&tm3U`8CMJ5UF-b+-Nb z{rW+gL7JdCs2-vpqHnXdS^ul~uf{RRG02nY$$W%;gay`r$lz@J0QCU%FXCUs3CIb^ zFG*jLOwFd|J?=eju8C`+X=&P9>RW11_W|#7le@|NxaD!niR2T>7myc_;C$sE^&k~b z!_!WXPmrf$r(;2Xj&*=_z=@g@HDasSI#54Q-$T<wlLO$=yY!$Z5A8?$XGG43T%WZ* z3-m5SC=d!zCey;H;nb~!tpqpR4W}j0610ulMzjm<DmE4yLCxQ)wyMEcdBA+Ye8GFc zTh>w5p-I)GzCpb~4I>RB-J;&2HUM}?9ulzXf|(7O4WWLaeo^^UK9j{{xvIOW>!s-h zICx4mj}4CvC5{ru$%c~+>=<?ouz~>_j~|a;Nm)q&{%hKP%6<wMw?}1<%8rVSiX{a| zfsc-l4$$uc)`CL;PU%kRZdq<w3Vnq>LARh=o32flqNQk1LjrU3VE}NZ>PPueK*~>R z`OxwK7?W0gGJi4)bOIe%-)3qvwcz}|kE@T1)<|od7e6n46nqrCH=#G-0p$S&kd&w& z$sftvaNBU8W;tQlgkf#|HoxAkx6d-nGL&n|H8TN*>xb)2R+DvV&D5HdekuJfW?akw zbL(%?ZxZMs$^^(E<`9n{k02Y98<Q2Siq?Ccdmg|NYwpr^X|Wot2E0RHelB(wyP3_* z<^f3qk`5sbA?^|G5j+$R<r(!Ebv$`Ic_DTowmP#qbMoNHgZ;JsTCq)R^XNVL8=4!y z1guO0&e(?7huD|aEv>5@QaL0CngczHJ&Og;{~hWbY8j=BGM_M?kcLP@fL{7tO}m;# zx<|U#nAVt{X`g99&6B31=?<9>nNePpSKX#=1GS$g$R|h^kwyGY`Az}%2S9CtjbUTH zL%u`6*fOAIKn-w2tV{Gu^j9@kHN&*Sv^Imy@TcQXM`?X&Ju!+HrOZ-hfgYAJav8ap zT1>@Ja1^i(yOw<|drjP$IB`%M?B(j^(ik;Hu%9}vIj(W&9J-5^i<V$juxd)@l+F!l z8`8umF-k}j5{FQSP!j+ehz-O(=sxIAX`j+0pCq4V`DXcomY^j}AEv*jxu@Bq-J>lt zmKmi^sS}J}S7NWkqOwuhsrXcUC%Kcnh`NXh_K8KfB3yr1e^^9xMD*;2*$pm-%Rw@d zjG$Iy)mSyV^}F>I)(WezT3G!x^ffdUG8OU;{SFPR6q2H;(bPATHxy9cev5pI6s3q# z-nYMRH+#)qP@gN;mFt8Wq2{;txAubRf{E#7x<9@D^u8#eC}9J90~{#z@GuID0)#ti zKXO0v66_M}bm(+w_0Z~}EI-Q+jK%Da437*WwIj6|01N}e@WJ-M_NMkt?WsYh1`Wy_ zlnM3_=Sb&BgQ<h5U@svc2ncf#a}iUMrY4PV8Q)UqDfCP?PdBgBt<=?P>NSUThjpjT zr_EZA)`Mt8w6-L*B;7*XLV$UG9AzA(6QF=pKpKM?gE^dWIOD>A3j-$BPOJsv9MwQI zaJ5|RPJrKr-v*L{<f!r2_`4#yA}&EMK{sGGV8I#BH_A7Pjcg;&#?QvLX18YFiN6y+ z=lz`b3GM{<L(@YORY%o<XC+;qt_R=qx~g?m(I28efIFJ+knfPqgl0k^rI2zP;34rL zF%_MPzL0()ef5{sU%*`Mv-+%H&5@(c0k-{`w4nY1?q5;@lz=!|96b;=5C$AG9H?`+ zDQ?O*@;LHh++y5cu)koZV@}670**k?8FUUc4K)qc4c1N2PSAp0?WfkKR%$i1dVlx+ z?t$q8(<@LFC@@xoJ@04$@aL*S)*<(#?nxc>an#3ORllmjtYOxT`i=U@+R55Kb${xf zo1UAxUEMBYqp{H(XO5ebJtsSW3*c6hSChdR##_=`5_q5GP&qUZ8Hl`Gf4N@k&^q=R z_Zd62o!W~48w?u^;0|n3ZBlJlSXY=4VuajB-$xsW2I3mZ8VayhLI(Z*ClMzR>|_97 zKz!JH*qdX?v7q!Q{aWo>tzYNY-8SDgZ}4pJ0M@1EWr@oYOW-B&z4*QOOXN#r@N*JL ziKI!GNtgkd12X##?mPHe-L*P}U19HU>~E~s)&nlmk6NOUXpD43I-q{2zi>$5kmZ@n zGxuQjVBjP;3G}S5C9frO@EklEj)uQUc#|-wc~CRaL-d?9pEQ5aeb8OjUe>PBuhHMM z+_Z#Og;z1#ne9)LpC*?h$`O%-NWvTP8#01|psXaWB;wHkfKe8VEw&n4jl<@!T{2uU zfEvYQ-DKTQVE=lKbB=RX!>k6;Fww9eGzk3z`v<m&R74s{8A<s>`b3(9n}mA?dj<={ z24d$l&T0JW`s(^%`d||4#5$YSrX6e;Y$&sqSu1^&zS7Rp&ey50|9|FA5Ka(uWF6TF z@Qm<`@DuqHNlGK70Y|$FUyZN+X8mRbEYpT|ZMzoSy8_17CU75H6VwD_frk&{!+v6a zVxN<qlhP<@ltNM=X$E!%7McakIv;gDs$ZaA!0xm=|1|w+0=<EtpXa6Fr9p4g+lp(7 zYarhs-$1|pW7K2RZ^CbaiEJWo1Zcyz;ol(MAi`6^Q>JuG>3CB0qzZ4vTif;R`o6lp zI-Z`VuQXShkshRHZ`0l;U%W4VSN5*#8@L-duoi0}w~)beGXXOJvn_L5=8mB|hIaeA z{c4BWQEjX?9@QNMl%vJE6~-0DZ}xBY9d$eE#ta%Wh?YUiIEFrko=2QV1jV6&0O0Q7 z0{8;>lf);9))s3ExSzJ!ve{CqFVzF<?mCrTr7yPtc+0)_TJN<Ak_1V2;dkK_JOw|1 zJb-*308WGx0eKAl3i1jvZ@|0(admNZR=d>>_RRZr`*m^voC#;5Icd%_^=ImfBZ?y` zGAlA?VrF7M5A7536Y?q2DH3?UL$DBRX#CLluJ>K<!#&|1tJ!J>d(}C*Il8%qxrP_k z7uJcsiN0+gwtWEe*-OMr#B2O(d>T0o=rre$!F{R-R0PVJW=*^Q<^Gq#n!=iowvRUO z{Da>M=y~g5?qNRUI^?Pg)CEAFHzW&^6@`t$ekOh<9w#3shm*od!?DA$o3l1&4T%{N zvpu*ySnsNLfxE(!^po@#br*HN48IJZ-z>Z)yhh)x@6JohODjW`Au$9D0YOHPdjY(} zzr?!{ZiFgDm2&^%{f||?Dj&oKu{|?9Gk|pzxc^dPt}(Z|Tir|FFMZF6<HRk6ErnHL zE3u&FGLJluyp_0>I07>Q<InVGZj9U*IjLb%1L(7OVR~T#`=Wp@pnGI|WNfjw*c)pb zYk^g+u%GEa)4|z1sMBPTv&i6HdJC=vcN~5kek|!&(yO*tZELI6RxPwHw5~L)G*s#; zbx;G;0BR=-y$ijgTSm8leJSX@or;@^n?jmGf|8+RAHhce-!m=)m+?IOdH4eV0>9N^ zb?{6)6ZpNd0G61Rm_Yv+upZzK9}+&~c*gOJLUbV-jI%N17&4NCBt_sNaD;3^c6>s7 z!lmX*%~{?o?`z9z3)w(6H0zpm;P)f9%B@{hT~$xopS1r>`kC|?{umC<V%Ct>kg5R) zz>|bZLKUPJq=T{LR_(1?@H=oBT}G@PtG@yOtX-PtyXL!w1cn4g4;ww~SLUxw7zTzR z0uM04C6y9Oi9idA1?<OJQ)8#b{%ZWy*yZkWr&v-f0}KNU;J)2B<2WODpD$Hkst)@U z_UUWN*ObSI$B60p>3C32I1I2AzZKts=s>JWU6s0^YeCnbnn5)&_82?Zd$9FveYhdq z0RC-*SMFEt)s3qgkHj2_$<NBq0;C-b7$Z$26A40u5J?ykh5=<jlcSQOfIklr@VGoK zFt4}jTLD$BP~UCrHU{hgdqHhM?bvT)zX{WXX<!cl*qQLVNV`Y~fETzIxF_%@@X5)O zlL7l_$2s3Q-(=fl8yNGa>8I%r84ekmfjuLL7vha?j&BC6Q}O#@`(f*_>#&!Imx$p0 z&Jn^90uGHsgFe*6p@~Dm{>%wHvF2DaxbyxB;9t|frf_GtbA*3{zxSZtgXTcyK-Qqv zpi&4a1Tc125-W)TYyex3U69>3v2Wtc)|st>DnZpv>rHF7q1&)szg<7nIMnF1dac*1 zu2&)3kZq?EPbc2YzM0(v*8^8UtRRAZhemuOJ_VJ6s!gv=2U6Mq;L6s$cf5DpH{CZm z^-g`1A<D4Sywp70HQWW>jf{wl2vCQjqN(WRgyjUVkIp0J5y9PspIJY%HpFd+JJxip zsh_u>x0kh-6&M8?%JgOWTgF>PFhA3(Y1NG%8b91jzL|U<ejmOQw-aX~nur5Q14&do z75^Ra9nmMPPg>)b#xML@e(hk#V8<BK7*mbDM!(&#-9WWaEyvu)+$Vx3g2E_a6ah+r zHlv%-;0%pPVv-~T2_XTKfY}e-4?PflAR68XZ)|ioy4PCPT5cF_7&`PF`dm}43DoAA zYnp3fKF56Cnz}XhBH|*V9oLR?5}m{+09$ceali^Byl+b1l);^YJH^%F>O6a%{XgS> zMsP0JY3MXWS|hD3o)*u*rh!d&V(-MR&RU&Sj48&{5NZhE?*rZ;unzm0@in7QWS_{v zfx!Wv%jW`Tf=3NU4Rio7&-QWlalWp5U3V$$QW&_q0%|2-pJyhTiDLksV4q<3XYbGU zBzh7l?UeTUzWKfrwiCA3#@EJZL$qO?ah(y&wX>>bRRKRvOG-jYLJ6z{2KxVc6MKV4 zlXwPy1|NfpLFGg8AqC+D;plpFeUr1v33?0m81@)W0DvA{f{WmKSpTrTY;f7&hmePm z98?bKI{rEy>?1q`55bG^VsJ1VY<>Ltcx(%{CABKGs@d9X-D})y6c_}C-^Sm@j{lW( z)nQVX-xs!bW~aM*W_M?kkd!YW-3=0gbV|2$H%Ny_NQekXFDZ%=!uE7`_s%Bpd#}Ga zf9T`$xI6oa``$R`-0D!Z)nGNCS2pw&=PS;61p?yMLN5wtJ%QptG59L%7VH*4Y*}qt zZOK$+s<;*0if!+=p~*<>{esE`l?ycsHAo)es3294KsV8>Y*so;oFy<92Tp9VAX)HA z`bzqO_JVfFeaQ{|iP2S~t0sP!_+icaHSbT9pD2&j#A-I0Hkzizrp2J&cp~>iF0GhW ztZ1WvFx93*aff1ct~&Q3?;`Jh{C*sb1WhoUeqH{${OJ3m@27s4`k{MO_bOz$pk=$+ z?j~vzb+mM}^g_Xf0&Xd{ba|WQZBS}al3tKr&{xt|a)x|{4B7yA=AfSM@uA0u7w=!Z z|FYuCigZ=F3N*m08&@~__&)yHJOorT%iGA?RFzhhE-zYM1U24K&QVUsaL4dA<2K`l znhiA@%Qu$aet-MD{)7I5pjuE3dc@zozk5$HPBAviHp@B}b}sBw+NZQzn{I8omUJxv zec>(9Ezw>4UHoeMYP(<USC6k8UkNoD2?0?LD-_5dfo7xG9$_9~)(Y1O2j&mVuPUx8 zhPrHK>CDnAg;xs4$j8V>vqrO=ey6{)p|b&U%f97(%eC*d@28eeEzhaRse$-+D{w1d zVwqS+<wxaz6#h{-y>xnM3&N@5Q^m-BB(IgQmC%{x%z9vbU`<!2tD(1mD0d$gApBbS zYvp*&cn!@#bKE7|C3r<%QMNK$$tmHKv})6;O}Hdn0v;(%(k5vQy@p=sE%aja7=4%O zF4e%n#(ltjNR%haGiozx(dfZ6Dl#f!;+Qx;D1K0MF6vygtaKSz1C<7gf<?duL8UBT zS}(0fvSUkIRa@1GiW3!xzK&S)st+rwR#ctQp3#2g`pVUT(t*-R(n)epc~1#vk*-u% zI=^IoiCt+|?vd<~G*TKVI+xBB)5f$=13_FEgaGFWK}AsgYyQ^^JesS#tGso&>vAs^ zT`XEvx~g;(LffLYMK={U6<>0{<SvUYi~ef*)wHB`N$uc@!4)GvjQlX5d_Xy9M?l|u z({t04P0yy!m(G_CFBo31tz=utwbE;)#$sb}Fh7`|E>0KkC+;VHaC~sYG%?K=RbNzj zK6pNyK=@B5ua>J1Sr1tkW-QF;E9fg2o;N)2bJ6FbMWu^MLnWaS(7P;=FOl=veD;Ij zgWx#hI3v{1gbG5%iVrJ3ye)rQ-lnEa&3MCj!+igIKX86`$acs~1*QU+^UN=uU)r;{ zXR$BOmp4l|ONhzA<TTrwZ5f&j4dgqBw(%j15UY$;4%Q6TthBGR55o<^y%4?-QuC?# zgNp|j!@K-_$@e8E3r-e5?*E$cnt|vjKA}NqkRe{VsoYe4>BFTDV=Bf}NNOduYNOhC zIdnM${ug`Xd*l-eClo#@c~T-Nm6V<-I#UGi^RN70`4C&iSjSk4)J1BDCvaUxl#eKf z9CxdBt9FNThjS!pBnf(-ukv5z?<?L{+@-WjX{(Y}C1uJo<x9y+iGrq}4fPE5r07%h z$E%N5mz9^5n?IO81S^6S=onPZH_tZ%@p3A6DtEPFwc<_Tn?hulQBsb89@WD8iu;Nx zZWR}oz$L(gV~%Q$3i6+(4^1Cll)ot7TD`Rz9r?Pq?zirl)S1*#l2MYG%9+X+#V?8p zrG(Oz#Vd;uwK{*0c#xP$W|EaIrK?%ntYuU)sxOpZD3>7|tUOp*tS(k_tz7HX#;c8Z zKAt}+cT_IqUPwAy@*P5cVSXXxA&45vz8St52JfSLwfAaK+ogh8&Mfa-(YfNAnr~_p z28CgSZ-g&KkI`31S4iuX^-5Q<t5{v4F0mKci>mUf@{n|0I1oP&-^bC%fze{LSE{a5 z9WOtQy6h{;Ayy7VhEl*coR680IV?CV;O24j))uWTf?0GL!Z!uq6o5C;Ip#U$^1$*y z7h@MAxt3h}bH&dUJ<5BOBUxZYzuJDa$Bf5}U4mVLQkImpQMOUGs$f+C+?yi^P(QrQ zeVfY?umnG6{hW2!cGy;^Db)0?>RknWqiTfas^+Sd+Lc<f!|doq=tTg3g#W%jnxP#l zIaUJq8|F`8Nmx=$FQ%ho%u6%S3=ng_ulT-VW%<hTVU@!wyQ#XV*k-o*RpeEK%As<W z$(PA}1-=4Y39h6!!imBYg?AKp6r*{gdDH8s*JoNYtuTj(Rz@o?mtQW2y5?o|%j!GG z1g(|3mHRsRI=Q2`qZrQl`l9+GxIfrpZ1KhXi}}E(okf{NIpjX%2CaNcbxZZsim4UA zF$h!!DtoAVs2^A!SbnSftqvWR+);{A3ebPyOYkNBVt+AeTNmt;?~^a&EaaG?rsz-R zpUl6g5GsGEd|v*%{AR_?ila40Yhaf3wfAf9EZQvERLN9Hjxt9nDi#&P`LVueeG&8; zup+DowfKm~oX4Cb9Z9#NdPnt$iV+p52uG@pQ~{4+v2C%fTSm8xDf}sXQLZTW=fa;0 zQ%h1ywiRzHMihgBv9hr;(0_IdbqkF%jWeaHQdN+PIm#X7#g)aC%WId{PBl(7rUlXh zj~I^_OQcJrhn0tw$iAca6N01AQFtf!PA<&nZewp_tL#<wB5jct>bxNpLn<a9^sDYy z4LzHIj)9I~PB7=a;JrYftIrJ<1`8qApdt)W4p9=NL@5J(X833Ll}4p;UhTYE;GrTL zk%~u^k1D@aeXFW9)tVGxMHty@u;$C=%d!fx3WgUCM_K>9;_ijr3ok1!E1=$l*wV$; z#rBQn8x3lCRzbfISs7Gh)nwIN(p}OGb`5q_5vzzS5lgf!Z(AO`i)fr%e4^+?(USZn z`S->5#XG4xsja=Oy{P?QfVx9kDXkn<F|1-J;!R9aPf|}sJ{F3&BEFZimor>GT)wDa zQNftvF~w654i+3NfY{f_Y2?gG%u2kqytRPe%KEDHRr4z5RTNYfR94qi*FZmblV_9X zU&_Cf{^I^(em*}RdMsy(&lHo2NJYEycIEXK^%or=9U!5!+{Mvz^z&-w)etL*mB%ZN zS465JRns-oH5s-Ho2F6In9s}Si4|hSyMlKG!-|KYN}^RU@P?+yrpTZUQAU)J)#laa zq$;TbP5qvVJrzG!{#<#z_IhncV@G4sm-O|b_o7chbH%IqSM%2ttto<D_xi&1g-3Fa z<o+%ATVTSOa4aXwiKqy=)-|nbvMaMIy%pYyv(;y-*J{^l-`d~Wxfm|yJpVl3rm!g( zg^a>4i@z*BT6DAso?}F*liC?}1~!Nd_BQo4O;Alxp<7Y)pyELV3SU(RR0mXNO=nH- zg71RUnA4b3q*J64r9^q4=s=O6SWs*!uoN6r98{o7#s4<z+pJ~wW%h5h-)MiX{<#`g ziL2~`FuG=R%}=_Ybhn(hobU1P@kv2aa4`2^?%2Yyg~DQCv7ks$gyi7)$o5IRm9~|( z-nZVDZ_GEA*Ou49dki_)qpC+$o79`sEDOuhCE6w0i`|PoP&QC@KzTrUvgl+{IYNhm z4h0+J8{|v4OSo6+uhiFD>#g7?Fs6D;HJl^AA+Ty$wYB<M{Y&>tH=E2R7m12Qg?WW} zy9;+0LVP(}c(!m{-nhKAqP3z5#36?B45dPbk+mahYbtBdTUcBPH7szuuUoHMVb&ey zggGL)NFG!MmB)*Yqs(-9(Pri5|L*e%_6hdj*x=Y%%UMfM9aJOQWp&5Oj+Npnah12$ zTMPVFtPkshzA~I!kMkbq-736Q2%11rA*pa)?!4SAVV3Y1;TU12Yo@D<zKb57e`H}* zxelSRy0LnXc8_+veY_o)fy-FTTg-FHopK~sFW6PItB8Q`GXG^h)PBd9$Cz(JZ$nef zQ_VxvL)4ZkOVu}(-&Foo^;6X=)hpE!(-PB#z=gmk`X~Ab$p{IuBgubT__lCM(Uzh$ z1#1cxD;6u(^Vjo7VMk$qcl_=EuKm`Ut^a)&xHiAk{8IC??q{9Csc`Pd*^yJhui!)9 zW>dkYf<;A(iheBou`rpJ%!`Vn;wkhg^z1-(pu4HN>6z-83N-kKDi2lWRp(Xzrv6QB zGMmgd!Z*S(W{e4%ifj4T^3{dv!j459i_}WBa;|)?T*uXMA-3e%^X!m|Lhc3MqeE4P zs#&$OYRmQIdXAgp9!MNW1kZp@xt($!6g(&xR5YkaQYb0p=kfD?68$6s&2gEx%nR=v zoL}I1_o4DbC9>+M23^ND)^Du)<NM<`**Dod8Bc~)VwKuLEn>9W3-gqD%F(jXGA4(~ z`77~P;+6H4)va-Bg4Mxlf2F_j=c=EpidDrb&_Q(bcJmfc3MgniDuVME&L3_Ox9Dua z*#bo8%SHBA!q%kLq(|;Y?%syphJS1St=)_Ge{_UNHIr(7*8Z$*v^Cl=O_(Mm^XEEc zPT3UY6lFzWMd22NMfr>VyU(aBVF4d!lVy|TE6rD$yVZBAaaFh~el@>3Rh_CHX&Py| z;J@JCPuoxHF77TKoHsb{bHV2VYoWCeTYxP9J{6Mn@)zJ1;1bS+bG9C#c6RN~s-0DH z5Hf2sYwzjq>25i0Io@W!&A!aL%sVYVEx)3?qI_KVxR8l(J@0znC-En-fFWR94qgt9 zH;*^pQr}YVs@_%IrK(HS`Reo44z)vl(|pqmn&H!o(~S0#_L3KQFY@5IKaKF6@;l`~ z`9OIaUK<`Si<k9{;~U3W-B}&vKL@K00u8n5Y3<Y68hwp^mur`6Bz`15%n$Q>D0(Ph zMhia!YEL-d%0y+NDryxK9sB-)rh%pcG>4v3J*RqU)zYfsn&KMp!w^|T*89=>(K)O+ zESi)imFLUzAzs3)3ejcryUV)EwsW>~0`-CVs4Z%9YMt6QHE(JzR$Z(rt}d=#uUfBa zZES7a<JsfcLfS%N3)w=ILZ!eL;0qQNE-DlihzdR{J}bbF1&z1KG%w9N(m2xiK=nX1 zx_Wf={i^#_uWMe{ywSeVUb9`ZjYy72rgPFcHmOazK7V~aT$e~eqyW+K@)c5r6y_mF z?j8dT`j^@-wfk!J)c`N#LiL5}0qOzj|C#=0LbiWCGL=k4zgjdgcVcb{nt^mG>{NJ6 zc}&SrFchG*pb#hoB!PD?GAuF-Qw>uMt{z;ShA_W&er>s~TnD*=E?t+tn!B2NR(4hf z*P>NntHRv{y9>tWjnDfm{w)5R@i${)cw!j5nJ#NCYbMuBu0bvQDq)SVMywHQ_M7*c z`vm(0yU@GP<zl(GI=4EvcR}xh28341R!a1r<ZF3rc?nDcbJBUzd0u~BpF~{V;nl;d zCn0cD9F<*f*JE8+7dMBS^OpCP2V5qo-yH>x0yYAiDgTK65uK-<r`-wM2|#a>ui<OD z)O4wVXa8!=)tU@#hBjh}SUN>IMR-ge6K0%U^Sb5@Ef`vG0|9(Ppa*Z~*f|hegbty@ zsdMUnsr{vPO7#>p8Glh-tEyFH7&D9!cf|di@SO0G|B(;6mkvmb_q5<?!8zqQ<rBpd z#RlO9A)@tBR{K`_rkSRhfa4{pk<?7Ao><+bwo5JaU}o87*&5@G@z<=^EQ7=#IgxiF zZ*aljf-wkpk!K=VN|qL}i`eoydEGnPI~%eJ(ao)$Tf3%uO?6RCQOzm!DRl=^2UD6a z&9|MrolFzbgtHa16%&;cm3s^J7O<2oC9-vpgYJ3(aRG6!XRl|Sah<V?x{Dfko0qFE zS8HpvwQ*fsm$WDC-I}^J_2cy8z`WI&=gjL~(7hn3Oe#O+e#)&A*NMk4$1vMO+eD98 zk66K@0Qi~bs?Sw}m(5({kp%h7sKBT|CAE@D6;VZYg<Y{+xm*d?cUS(d{D>?fyT`l7 ztIw&=(Ymy*YldqE@Fr!{Flw5ro2oadHmN@9Kk9#S{^VSiy(}AgpZ#S0WbiJc5n#a{ z<sN0%+^)HAL~leR=_Bd=Lj6L}6QgRWT9_k3F@tFDwSBaGwC^qNEiIvz&|CUj`X|vR z5qK5BxeR$~I4_*nAZ?HqbBnpCot8NYnKOIz9{qmReihuKaR}|y?bI|Q%{b3J&s~G7 z!5Mf4o<ru4O~{{+|DT=#^#~U!7Af`%_X}B67S-$b`d6A)nonp>Xp%L_nrwvowfAdL z8&KEI*3O1xF0t**?aUf+jTmMKTa{at4-f|A4akd2;*x{xgY5PV?HlUsb@opBPWnNr zL8?wQosd%XkD8b|rtV_uVw&uo>~#@cM0jr(%NNVH<ZsE>C^gEX`A73l$WO?72zm$( zk`9vWUb}a<X}2j~ldpmP?GS{)s=+FeUZlTmzio$k6iR>C$0WxjpL0Lwu2rs8PCz)7 zdny-6mc*6JN+z;Di{H21x1H9V*3ngT)z>v&*T77^OjD*=WL{(jZu>;aL<;n*!t${E zr+ftEPfBb)HvgXNp3KU#@^<5Q<M|%G=Z^7?5z(^L_8NQ5S%k1EtOCA&UuR$E&*?v> zui&iU^p*CN_R8y(H%mE7S(jgzZ&6qjpcO_`O?o1n2p_c`wc>O*U7$8lyRBwh&F$LT zwfnXEwNT?lgVEq3+9Fy=7!poaOjZE5WRh}{a%JAiJm`Ba;4a`E#2m!zaqV%14PnDd z^-8tB#*gk&vgWk<v>LUDj7_d4*A46q>_hHDu1o5YuE|@I*H76`xix=lK1ab(926cD z>Zm&EjNpvmKFdA}{BA^(uYFSUq^6^)qiUgUp^ji9{5RPCALD<F*P_>=7m62(`}z0t zVGam9C`1pESlL!K#1^qr?CfgjYG|WwqyAX)vF7XAuWLtWMra0@2AIk{<(@)9A;HKq z@>a-J$S&ty&f_cjN=!Z`AI`+#g5d&q-+TId`p=lpn8CB>aqZ*UmYSBDx2m_Q@%r(4 z(2~wg%uQ&R8YaxXsJYZ!xGs+nigSx|{}BHn?!@ZE+Ecfu&R{p#PwP+X8&!=eb}buC zZ(rBKJGIQb%nY+w4M{^nw~=2jtCzh(hI!y|2DREE*&<mhek*=8p_(wqJIDLN^uh$s z$KBeywS5t0sAs5$8HO1?IX*d-H7#rE%kInWDd{Nz-81mn$0Dp%tW`k&d=cX7Jc~Yy z!i*<dpRGqD6;;pLp0(Xo-BlK?MSH_?!-5augWw;e7w82rpVa1Q^RDM#&rjwib9YI1 zNl$Z6bJyjp%X#g7?QSqO7-wi^Xdcx*sy%~%)nGMn-kx@ycCoYB*@rlXI8KREay0j7 zF5KflULfzO{Hc70aEEXMZ38VDiiR#*FI#u$cId9FuB(>SE~^E;&qLiq9n==*!so(K zT9o!k_(=F({$6g&v*k_8pO!x<cTz6AS0avxvpaKl=1A8_S5IS4BUi)Kz;$g#c%pft znQoeH8tWPB8HyW<d&+&vT_s&5{VVsc+{yWq^H=1p$V<o)vQdIjg0YmblwShB1i;%4 zJ$|~Qs-r4+_92Hqr9Y(~WglfPi<QMrGEOqKh_;AOYM?+A(L7>4G5@jRvEsP+xcC6; z0Bd8z#s={4`qA*C!GgqJ)>>=rbk%g#3hfH*7V{Q!CtoLDPhwBvd)|9qi?l_G#zDD7 z`9=Blx%IhlCX)ChzLV%Amifwj73K=_Z`$9qdsKT=4Ydumkjw2d>@py`2uE6dT0MnD zVFB-dp<<x|`bc*Wa0;Aak7$p`#4s`L#_z^!?X~u7L$(3(Lg@Fa5xVHQ=-}@h92gw< zk^Ce1Z~ou>#j?e+kGUUn<9YGCg}DoJfq(Xx`<UB|YsO`Jv%Tr&bn_VP80}uwUR6JY zt(vWxEygXzA+8~=1?daYXR&9o!Q<$<;<}<NuPkp60`%*b2$u-4bS(XF<Z$Gr?WJv- zew%);daoK$G*qM1qtsYERv)wmt(QWVLZ_*xsaFJ71ZtUDmYtW4rmnx`=@oj#ILSE4 z2+jzO0;9m}a_@2vG7U0ewOH*5)d|%_1n9p&90m>nvg5~|;+*2FkgSlP)}aE<dg!ws zksXl@6buw(Q?sdpkRbGr^&cx<kJm3#FH<j2El?pDg&NV(^~g5BJ}o*eI)*-mzFfFm zSSl}-!*`j2&`r@z@vZn<F?c0HY(b++*Cpd6<G-4JHQ=$HrOr~@v^MQo^I7vF?<4OK z{1N;u?k#SMq(zdFo02;(Z(iQi+^M<9`b0|O)A;X5??{vYC1AJMEthqdb+gp7)HhW( zRr56SH2)g@HJovrahMb4gn?mT0H@}A`S)@-M>6v=^N!1p%cqE@h``qX_8K%fnjAPI z&Ui|5N>h&b0xQ%j)X)!jZh3CG=D+4gvLn(B-VNR|=`tz!+#$+B9_Z_$lBfjP>2pzQ zmoU^f)JL^YErWD}bP!WoR4uA>ZMycU@u`vGqPT7}-DrYX27Vau4uhZR&j>D=OEy9{ zLb#E>kv=RoEQTz3?4XBOs#&UOQZ=bU>X3SlevUqD4O=UNmBDV5Zj>MRKkz}XlaI{J z;t0bO!xTgbQL>D)jMI#5#=i8t^o%o)Gk?;4(k@glRF6csroE;uGnJVxxG%ULXFbkZ z#$Lu=EM6>*$RqN1x$jWr*(p~kQ_8*&d?7eTJx9F|z7V$BtTylo8>bnkfu8htn(s7l z9+cQi>;obLB001i8fa-d$vVjja|?5?B0Q8oluN`CaRxhsjqJ@br?{uMe>44Nf|(oi zNA4hu)s595>sa$p?@;ewIe+CG<Q(KQiJQbsWZG~c_d@QUia!-Ir8A{iK9;|iyqCN@ zxIEZsZM0s{U(om0^w+@O)TC+BKyPBRW3!`Ayifc*{XAVP6bpCCcFN?ra?~*&mCKMb z<oTj}(I)05W_d$-gV-f@AqlK$r*^0Iocf#^;vGE4dW+s7^b7sa!#K=6%v~y3Dru)^ zr+|0)E5%oe<C5c&N^T{$lvqkU<3Hn9SQXYj`aXK7T_30)s1IllXj4onCb!e=gr0F0 zGmH6N_+I!{_ErXGT2hfzERZdb!93VZH`6;NIwn$_DbC%--Nq@}DOw~OP#@GB)Kusz z^q;Mtt-_!%s3xgN>v-#U;9J^T(OXfPTblbs{zU#%{8YSyvx74OHv`wo*UC4`GRu<G zC3OQd12h-}$mNozq-mOan!9a!+w=jf0W8pngC9qOqCv4A;i2@Qlq#SKz^lDev{Uqx z{gWM0E{#34J+;UZM-5z}GlnyUw)VF6Dd8z$v=V_bj6aP3B{JF=s2Hg5A)s_ut`TWO zdF(v)!|aFIKX`udl$p!S&=;SenV^}8P^+ueCCy25qo>jH4D$@LfW3en7sW++@;rH^ zq7rNs6it#QNgc0_hbTo9d&nLl+sXEehKq(0ZHcy>rk&=n_ONz|afxw-V}&C%mKvKv zn?fTANP>OReNyl=JBe^Wc0lG4dV~*{510^J2Dt~h`DVU(oo=0Oxn?<1G`!Pn*KgOS zT2rkzeK&n^TpYKQvy}6l_&YIpP{2(4qWq$~jiikP$qcv|qzqCd5D9d(b+vtE_{xBe zaP4Z%Y7J`t=|-4FnErMC>zq(Gp{|-<O@A(UE<hHk(hiCaivJ<_q&{g-5ELjGO2&-j zjHJS)a2+-sHVxMe*Fk*gtnIArVd!Dt+PJp8fxUrg#A!sBHyOo7@dWt<`7FgOg+MNl ze<S`zyoa-gvje{a-^<_2zs9=88q>%01=<4bYt3s-nXXI+24G*fzi@k-yiK5aoG6?q z%$8-#_zJ!PYN{^ME>aiY#otWZO!LKjF}{=Uq?_obHo7*tH<~vZ*d+mVVlR6y`?=7$ z&~);2awlFVUROz132;T>oIWf&EZZ#FEc%lDC3_im8TPFg0rjWB4-@_tA;LucL_LyD zSU?{#E^Ay?Ushk1SLhYCm$jEQ$(zuIlJDf;S6aoZ;@zd(rA&%UiXgg+<BIW$k*DM7 zT5DTtp+8(}tTlqi^2f->2pY{$Kk`2EK!Z6|K2@$lfHVKJ@U##(e%=gk#v0EW4{GmP zK(9GcJ5swDA<K|uK$eo$QNB^W(wx$q5$qA{S)y5@F|si-$blMVjWT3QB?b=WebRl> zpwOTY<R^WNeT|U2^wRdyKGQwZLCgYfHYz*gr)j5YH~2UBsO*xAl8=&aM3^L<B)ulM zCYa8c&X6=p8i%-txM62QKYc$v>=g+jJTW{m!2S|Ar!0629{3X~kxDdAHcxg;eoVeb zwnkPjsuzKlZW&=2VNYOB;GpfGEpCV#XgZn>;>vLSaQz|kAv4R(a=)p6Qy-#-=z|1< z1b8W4`lb9!xe(!=<eua@|2n^lRz*V=eTnX_?k+?DGNa>Pe_nfDJ5@JTx7N7UIMqJY z{yF$L2>cZRSHSInIF8F@%Vm&j#H2APvhfo2VE17Elk-nbnm^4C+~ymG8wTJ%IkXOK zNFUN~uxzmG@a*u^rPZauGY5Gtve=Sx<y`qg*+W^XBvo>OcY%jWYpOTujSg}Sa^5oC zGLiHoy-KUn8g)ipPg76R9>*SsBBF>u58KIga`%Y${P$gQ<T>)G(y7v(!k)qiGs65T z`>X6<y}x>qb*go#VX2{mu7eKQbL);6ju=*1S6T5syzfiQmzZK!G3#sL*TTut$<mlC zChLRHPux$um%EqSm)w_pJA6C5$g#*V#5BZ|u20v)^_`}lrl*>zW@Jy`yb-$*n@pWd zJ;6J{L#=P|T-jXNBZPm@VxsYa@q*oq-3*8=t39hdzgd2>KwN-x>?#7}&g*RJY<K*3 z{QYtLaWb}y{SFz3ESD~qK9xO{y_3F^Lf$)@Gn?}T@e3k699QjE?TyApBYc<1y2(0t zx5MVJ8Ry2ijdjMl$F#?^PW(=MoCGI<{_P|LWN$73e*iokPuC~)NzmyOS&A%TgV?YU z>F}9)roPG8WZY-pX9tbXNy14&Dkqf#GlWgjO;VV<LJbT4;a{-7U{~X+abE?#3M6a^ z+waETjc^v;)ZNrUo?L6GwaoR*_2f3^HpBcFbT1GWI><W6>JjN;m3WnS25$xrysV-3 zyuh`<_0;^-j4@yga0VRGAJRWIJvLo;Tz9MruL`dxttY+XyyL)i*eu;Fg`DBB<gvsg zFbNXO1k-_WV5~l?Z=`LcZG~}#QLGmuW(Y<Pa~9}X8@vW@I5V6HXX^mL00GRL@G`s% zYN|g)e~QL)$8#T(ACpl^7TxdM@BG^QwHf*}VO>~9HjoY5&D+h0^6o?{c%r}=+{oR? zeJ6S+S}k2I^&-e6a!Cung@2B5j&UgCP{w8NW$#PtODn{M?)vWf1qjthzxcxT!Zyc0 z$A1)i6q~|IVSx^Om1LC!@-YfRx+q-)yx(eKHL*Bc97gtRjx=+cxmn+=Z;t@y>erU9 zEo3*@y&|z90h%r`Pt3~{XNn=`If$@Ryi-i%6Zv1zzo4&cT-OL3ydKsbRwRKj&eG4) zYZ11YwweC6|81uQX+a@ghzH+vflwgaCfO!AFFh~)LGpv7o3NXZ!{)HB<FDg)26qMt z4ua!}>4_=TkZRbf->Qe60<zY$PWDXpv}<VBfNb7qcX@Yt5ElkW2Y_5sY7^N+Y#y6; zk9v<frfy6f%(BZZ<ra()V?3chp&w!xVgPO*3J(r{$R9#uS>kW(-`J4<@09G6K%9gg z<^sV2ft%@O@^kn(tN<%8&pyw-!L-2yGkQJxyfeNt3Ty(~Gw(C+owPe?kSDcAX5|yb z6UFGUm;NdFQ_@z{R>b16xJVvFxfHt;yXm^=nrE43X)&}Is`OQQ$otbQX_iK3qq8Z} z6hZAa(sa&r&NJaN;U>u@3CxVwir0!Ef4;!Dz<8bYI!o<S`%u|o8)_PALX=&w@-Zwl zEi{qrBzt>*d;gv6JK08tk#UuOmA_oPTnv4%VF<vp!g8>j;iTcDeUW_;yVLH3{2$r# zp}&Y`7-1Y?th7{G<ZiipYJ6&Z0A&Cri<`v-zoSKxMUpZExVE==w|E2T1L+W39(o^o z$Jxf&@=f`sG021U5&{QlAb^wmPvD=xk(?trFPJZwIf5L)7V#D_{ES+0t+-53CWx_O ztZ{^KgvwB5sK8m^L~={>XTxX2E}Uwx7%j#t)+^R&o@t)h$=S(6)I-!$+*4fWxsO5| zl?e#lMcqX&xi7ik5i`DieEmbuLyyzyv=U836VxMgBi#r!HTYG3ANoG@2mTNIKGr_g zG{H0h@EqVB7%m<zj_@OVWJ}CMl5O0W;F#b}$4<w7)LSPSiN^be`-YXKm8KoG9X8;+ z&2OIH{5$P;8kfiA4Hpd;l}Jh?32{PvTzFiFq~M%!lyMYf!InU_pYBc8P1e??)+W#` z955a*?zil><h$}+QzBC$yNJ7puy5p7!LNc%;!R>?7cO}vdL`nc&k@EE#sJI!3?_gH zK;Lqbd6F4o3Izc@V&+BmMRuB>=HHOHA@eT%F8u)S0B@dXo+w3<BH1e5Ds~7Qf`8cm zuy2!YlakS7G~J!<&bQ`UKN>$8KN&t5NG6hLly#JKpnITuM{Gwdhnz$9v3+c>z$<|I z3TDPofA-|{<PD(@p|{Owo8j`gd`s<1?H$b>&EPS(8UbSGbjNhZ!N9>l7!$_8yeYv; z@Yaadh(P?5gOD%C7d&J=WLXJTf+!-2058FUKB=ZuQ$J%r;~CQ#lf)*mL0*JX>N@Cs ze$V-y19P6S;<4iO2tNpa5KiPy<R+*Is;*JjX!F{<t?aGr|K-c5@9sgEZ<%lT*7>ay zIG-bMBXDz=bC?781Na+68$`RsyT#BWnaQ8Y|B?A46H$6`$3n+KSDja#7cCbp6cfb+ z{O=-jkr})fpq9VVaHRqEG<@cK=5!Z!7q%6*6|)e+g0Nr#X8~smWeX)<7q4sbG<i<j zPTTNiycy1;$)?FB(A~^)&2xFeo^X3YdjkBANBBqht3|6tJ;Xi4siIWTDc&jGa<s2* zE_N=qN3chbhWr?oSe952t<97)CXH{*Z_Gx!(T?$B{0q_+r0t^ZqQMLv@&$;yzlwep z^%V3JKuz30Y9K9$FNiBWN>6WFZ(FuG+Z;7UjYgBv1n<K}_eQrNYKXoez9GU)q>^9B zpDUURw?nj4xK#Kz_irxvqV+;cx)=dQ;IQMcV=&sg0`X-$0>rD=j@OPwfkgpfwlI4M zeF+`e$8qNf=Llh6TL*-_{Js1|tVOK;#Qwxp(N)pK?#1qAYqOPLCYU>$I-91Mr<rfq zZrCn(E_l%El8}%k<O%Ev>=Z$Y0N$fx2s(jI;Nf^Ub<{d4#1?{|;D=dJ8%rC@TGLw7 zD+IcgZhh)}>Wl;<!76MO78V2*@(Otig$sp;MTbRY!ZKkwubel9IfXe7KM#-E(&2xR z=iYPcb1OVA&^L;iW9AL^4fbkpwf9r<Q*t<EI0cpEY=uA}fV`s}0{8&8=CtOVq@1Ln zwq!jjk$g|=PwWzl#B$qo+XU~yYwK$(T6N_*5Izt_Ep%K9qlK}8w}J=PWuR!FsJ*bg zFq@an`-<@uqfbtsoOPjfA@Kg(Z{2V0VD4bHo9rgoVKUA!&Oz~0{CAu0Hcy~Vpbq5> z<-p7#MU*0XBYYz~%s<R0u}N$sKPT5G>JuZqBfT^2GwtAy6*h%Ucg=UrTWniwf4l#7 ze;xfgx{0uf0KU1u^M2>S^8)evs^F>sbY~nohkiNxa`u4WfZzk?1Lqv;94q7<Um-w` z0NL(2X9i{lGBPtV`_lT-?r`pKh6#oVpf@lU!N#-k7%T?s2=NFJQBPwhJtsXpJI^k) zNG<Kn?afFQZ|QCCZ8vxf9?(j-iEd&$Ry!8_e?5dfgclK@j-SDq!Ld<o)DP(&(z^w^ z1$sMsJBM0_TDO_EnSVogZ+<I<!1k9_)(gieb{3^lWZ6ceG%%0A>b8fl&ZlATlB) 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 HdUO8|rKz;W literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-cover_d.png b/Templates/BaseGame/game/tools/navEditor/images/nav-cover_d.png new file mode 100644 index 0000000000000000000000000000000000000000..973f360eb978c729f0b977c62959770fe7b887e8 GIT binary patch literal 525 zcmV+o0`mQdP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~00006VoOIv00000 z008+zyMF)x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vI&xCOA<~V1fVu0h&of zK~zY`?UgZZ;y@UMKYPYA1PLQkQCcDe5{Z<SQ=o{7D{u-<;XQ)4^C4(xsH~*`Swcu8 zO(M`P1rlS}4#8qbNSsvrBp2iPN8dcXe`c&82p)h4@X1M*W#Kpujw8`ZPnFh>5{PcJ z@@utQJkP_nZSvG|x%7YkSw#}?YqeYM?(ZqNE>g<O1;&`8xqrv!WQ?KT@B21@=Xn<) zz{%WOknMK6uhUv%1K76xuH9Xu!w7^B<QvE(@PE*udc7W%N@f35t5xRndHx_{4Bc** zBuPk;gkrJCdcDr>+QW9d_OPaDO1WId^E?2QQW#?{J6f$)A98efEQAoGX?hvty6&ez zQp&d-eMD)RB82#Q(6>3dCZnSZZZ?|{hT%z(5F#_f(P%`o**tO-MG<iv)9G|5l}g#5 z&1S=5vB=C~GMSwGZ!{Xz>-GJ()oL*wj~Nb!wA=0D;vYe<`xVE#uhd{L*x4fI=i%&K zr<9t$zP_C8miIQrIXyo;0Zgr*Vfdi5jtaWqYwe(P0VySxW#wgN3OxP=#q02c19)z| P00000NkvXXu0mjf{VM3` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-cover_h.png b/Templates/BaseGame/game/tools/navEditor/images/nav-cover_h.png new file mode 100644 index 0000000000000000000000000000000000000000..91ce9fbdb7fea4169ed78f52ab2efc089a6b840f GIT binary patch literal 633 zcmV-<0*3vGP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~00006VoOIv00000 z008+zyMF)x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vI&xCjyRGh;aY_0tQJ$ zK~zY`wN^cg+dvR~J5m!HW8v8O03Qz6IfzN0CI|Tw7yBQi%XO~P`xoQ~T)5VqJL4uz z%0P(D5#&f=EONnjF}9?g8Ktmw2O^a~+s(kd$D4gKyJk9_J^?TU;Gvwe25{l3>ag4E zdYxz8Qdq8j243(Z7`jN+YqyUeoRmV;ZXY37EIe04!In|x5mC6QDxx@s<4|cNsVZC+ zfr^Dnd5TyBuByUXhB4)kq$!}Pa7CnaA`#iQj>Q%NIeUNhW;UC>%3{ZH<owh5$9}*6 z_dl%@a8k^5Y?)48pL`jOMxO!T^zG@p+x4xR$5R!!%!0%gn<2~ARIAmv^{5pA01?S9 z+EE^lIa9;}GDWN_<PfRK-m=CRwMjx*3;>!Q6p1LTwS~;U%miaJvG$H=7e$oC_hwMI zA`D?JIE+FDMzhUAL>fmi<u{E8?-j8WGy^jkGMd#JV>C`;%Htgr39Pln6&a)UzIADm zBqon1*=A5x0D$lN^~q%NG7soDPFkr{;(R^~!&a}?`<2xbk<RDy7ryU59t;Lw8;wS? zw|7p_cDtp`X7eBqsH$eUTsHFgX0sV~I-TEHeZSxTzFMsw20`$2v)O#B)oOQ&%y>Nh z@cj6=)MsLGc?nlVpe&^l>EVvn*MF`(uU;?b!*Fv0Rb9K9rWaut4#O}k^>gwM<+*B$ TSfv%a00000NkvXXu0mjf)PxiJ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-cover_n.png b/Templates/BaseGame/game/tools/navEditor/images/nav-cover_n.png new file mode 100644 index 0000000000000000000000000000000000000000..4080a20ccdaf695e908e7e7600f30ab9f169a846 GIT binary patch literal 307 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)m!3HGnb@XfnQfx`y?k+$Y2!1;6t_M<_1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP~BNMYa`#+<ae?XxFo-U3d9>?EavCU!%6gm1)-p?&3 zSGlg?h*H8+FP+WO;S+V2Y$_0qbUff^&^tAWh5Mr7S!cGH7oF0b68K^&p6@&TyLiWk z6(K7x#b-B_t_sUY<-PWf#rC1>-+*e9Pi*f4TXfA1%7407<FY9E)O@Br76JQj3!iIM z>2l)gexJH<LGdCL&r^(dZ(UT~+q}JJr*NIouk}BCekvc6c*MNhCZH$noL-#KulO0u w|L`#P82r6oA86aR{@!&t)-3z&Kz*0=k9=32VIv+{1@sGpr>mdKI;Vst01Ov&rvLx| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-editor_d.png b/Templates/BaseGame/game/tools/navEditor/images/nav-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..066ce2367f711aa5073cf999086454eaf73bbdfe GIT binary patch literal 1321 zcmV+^1=jkBP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vI&x883=L&v^g<1hz>; zK~zY`wU&EqQ)L*&f2Zf1c5An`XY0zP)3qB+KoO7xFd*ZS7+k6eB5WhL1QH=Jf|tTj zhG@7LiGN{Yl}mzPM2Waf1(~p*Ak-O$StQJmz*u2y?KZkwyLP?o_y<H?HpaN@O}>B5 z`Ofp4=l$OI{T{*Xc25P$0XriQf*>G@B9bJbm9-7BR$eRt<;gm&Jtt=zilU&=Xc(yo z1a8{f0`27zV9&`J#}kt$lWH^~Nz#as#^Z6SPM)+&0E(jYBMbxrcsw4=W;2SSpePEG zK83hOW~?#!zDXrXLY8GD0F6e|PhnbG8hX8+HEY&TS67ED%cuybs2EKOCbO9olL?bq zA=P9eH**XYi-pqC(z_myR;S~Rxm>Vd0kyTY?Ao=9tvhxwdC^NuUs6mW8YL3!A{y#O zhy+RW1o8KDbGp0&y<U&Y<r<PymLzby-Ri{)7u2Dmv9VEI`21qkY#pPPe7sk!s`snE zdfU{WF8kDPFE*;X|G1*A{o+T}Xjas^y1F5;s;jHjVO5RIW~1u+GJdE$!3+6|$b0rV zf@+-F<_>yd2>|rgO!A6fWl7Nr{;K(X$b!75{>Ym@pCrA36BoTSb%n`uWH2Ek6#-Cz z$DIWvl2Tar_Payk+;dYV8&ZgdgLpf7IDNI1biIyw*;Y(RGJPs6e0>WC_U-1hr~2WL z8g(*ygMnx`j0*I`5}fn4aWxQRc9w-nRwE{hm3gbzvuybaB9X|$QJQ8pL2nr0f6p2^ zdN_5(PpVF0p2Nx<=ZmC{wy|=<<_9BHRh5$Ww{y+w#WBXp`1BMUiUGS>&!c8NX?h*k zJHv!xappQQcx~%PRDN}k+S=NILt=O{Gk|kh5!ajjZ2Y`}yr~mu3HB0-#nA`?0w9UE z{$C>qbcCa1XJzv8y7jzMx|!q0j@}z-RaF&Bi<UDbe<_8oopgvIo@+OmZd1705yICU zx#PE;2?4X&Jba~neSK`&@*eKb_K?5f1G1*g{P$gJs27j7jaiSRqnAbM1EE1NZro^O zip@4WQlHO9L17V1p<aqVIgCL`?|(-E-Mw+nG_>;cqZSOZ#07unK&35y(kITkdm**A zx8rm=@tix$lxa^9@;A|!Nc1nN+ba_9<7`7K#w3{;nQ4fE&~IN?tB<jRC>=nmC<-+- zHC(!Mi5ZV&^Y@XxxR=f4@Wxj-U-2c~EzSM*$NE$}-Zm1d${f1|o%kPQXRD8K4*T$1 zCObQu64zT)pZS><Uo);Z-@x4MqkQ8k_N~n4mrr+c?aylB(I_f#{zg0gV1&7j3{tex z?eq?=bXYB8Fc?@{T*%tuLICRP>)HSH5l&SeqGG#?QMr@InVCn|?1l8?=J1T&!nw;$ z#3EsIspk759V`pmHkGh#Qwc3CEmRym#{O?AIB;kWrqMQ@oIRhc@wu2XMv<B%4@|Ep zK9~`+TCJ>JwUXbCe@Azq4cFEWNlTWgJh+Dm*$#|4c@WzoK0G5PiX!U^oKWDTt2c&} zhz;(QWUV|R&hM7O!Lc<ut*)WHqn&s>&PYddQxiagP*zqpRj1RH%d%vbWi47+MwG<c fbLXK)1F-lXG9Bll<A}ZP00000NkvXXu0mjf!Dw_) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-editor_h.png b/Templates/BaseGame/game/tools/navEditor/images/nav-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..dde52710aeec391ea2a132f25db9a5358cc2edbe GIT binary patch literal 1595 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-editor_n.png b/Templates/BaseGame/game/tools/navEditor/images/nav-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6b7778bea39d8c94d6524a9ca794096ba32daa3e GIT binary patch literal 1123 zcmV-p1f2VcP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru-vI&x89L{mE0O>J1Mo>i zK~zY`?Uv6=BWD=Lzw^$U8SQ9d5{*uxD65HBgjG_qqzHoaP(+j$TT8*4M_G!{OQDBe zytQ8X2MAkAr4*(MLZNu66hbXRLE9?g62xR;jcbz0k4fg8Ob=Dry6&#K?(S*7mlxjm zeZHUf$Mf*Mz#F|n30HM8#(V(Y1&{-93gC2RW=8Jm>G8zl@jqS(^b9-(;A;SH1F*=M zL{TgeLJUcgs%LHa=;-J-09gQwnx@rX7<vYF0r-Sc`b9Jvm1D7($T@ea)v8dfR$*C| zV_B9^E|*JdYikc|+rFo1+E+J%J^~N5wYB|dn&!~-^fZk|qacKUa}LfqDwPV7$z*PM zd0Dh=`;Mk*f8Q8-uKD@-4|QGterah5j^iMk&4P1&K3w0}*vM^eZW@MR^l6&LpX5WH z2Rb`D`zxiCCzDB(N+l;448rU6UU<b~F}L6EZ|~~r`u1r)<aslXQd+B4tF~bnPCA`N zLqh{Xp%5fV0suNbJ}zgo*$);L7T$j;(0Dwq6GBYOvP1xca}HhCQ79Cksw%u*FPfX1 zg|V?QlF#SwGsYTT%1$}w|Cpv}0I<*d48y?T;UOeRLNFM_@bEBvKA$u=IQYv|9^!Rj zj4^>R_MKrEBcr3ElF#RZEXzcaBuJ73N-1i!8Yrdk`F!Z=>atf?SG|!)<fomToy>DX z8Dl=b-~YR1StE0Eb2J<d6UT7`K@bE=DJZ3oWf_trfe-@QwkwLFP*D_f2L}hk+uPgs z{+Acm0KO;7^8Ii)jFFKM7a>H@bsd2~Krjpg<#PF|um%7ql}b6saUMRFj8B1)rlzJZ z9mlylF)<-_cXwaZv}u~iWHM-PZwI9mg+k%V`ZrBe6-DvqYe5-f9WIyahnAL>p{c1U zswm3kDfL>dhIBejIyySwa=CDFa`KpuVHhsA+g*HS_A<u209Gx_x}zw{pSfJFQm@yY zE8>W4+t790kt7L$KmdXuT!spYq8vSQr<$gn0(gfpb}N-ieVk6G8!DAbBoqqCy}iA5 zI2?w@<GF~d*Xx3=>u76hgQ}{?WHPAN|9fbP#iHnTyZ^fOoobr)0Kg)EMaG!Y+S>a0 z*49>ZeSQ6QV`HPIudff`a9Hf@?4*=Z93LOU>-8cO3L%rpz_Kh9i$!r@U_ie%^wI)K znx=jB$Q9hmX0r>sySs7D`E6BINpEj2i9{j@27^#l6{%DToO2--i@9D1`h-)sd$xc; zp-}i}e}DheL?Y4Y@pwpoe?L@Jg`y~26vfPqp0;b$v^Y6A`OV(mUeC$N$xuF@-(Fc+ piM_VZS0a&68Dnp~`G&rB{R``7n`X~ogy8@H002ovPDHLkV1nK!2owMS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-link_d.png b/Templates/BaseGame/game/tools/navEditor/images/nav-link_d.png new file mode 100644 index 0000000000000000000000000000000000000000..77f1499b394b0ec80313c996b0d12ecfe026dfc8 GIT binary patch literal 692 zcmV;l0!#ggP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyW1 z6%sF<IN2Kj00J~gL_t(Y$Gw%mOCv!P$3MH7-Psr~YzhS}!Y;A%1hq+eAy}jd0YzK? z6H6`Zwlr9IMIcy(G=Z4L#zwh*LCuj6LGEr{Fe7`_9e9_YkleSLU+;Zp=Dm5Nd7ifk zQ~`$>Nz*h8!@w{E(v-d_Wl;dtxHO$iW{p%Tg|6$&QZFwr&i5Zbs{(K`nKeFdZ15?W zL<lj^Kq>WM?(hCEDWzz&T8<8oN~K0ZfPdyDf&5-4r9=Sex;~L%7=}DPKJxJJz~bT} zuIsY2v^3T(7K<^_wt^tw`1qJgrNY+M7O$_b)M_<s+a{mSb9Q#d>gwu9A7dlm>2%oN z-)DJwncLf2DwPW7=jU|0UE1w7hG9@B6zKJOqaAsk7k$0Ij|M3Sf~Z(5M)i9A!+5{n z|I-e`Fe;TwQK?jllu`rz+}zwmBTsa4a>C)^A^CiMXd)Pf!RhHKjYfldy*~Zb`Mysg zk)Tj0j16=$nPhKokBf^7y4~({AYIql+1bIetg*?GNF>N+voxDcI-SmRpmaLT_V)Hf zZ^g1K*4Nhw!;q(^C*I%Rr_Z`c6_3YBr_;Q>z0vRY=L6c^-R0`)3fFZBf`EgAgZY5U z<uc7?6Cnhi=V9CSd_XHJD;ymiad&rzQi^i9Jii#_aye|<#&zAn#dTI7$8os6z8+r1 zwHbbL3kwUwD?>~O{)cqaH2vr29!jaX0KT=`0KWF+@^aIZvT8}ok<!4F7D5O#ZDyD1 a1I1q(Pxeyyq>4cR0000<MNUMnLSTYj7eKQB literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-link_h.png b/Templates/BaseGame/game/tools/navEditor/images/nav-link_h.png new file mode 100644 index 0000000000000000000000000000000000000000..4fa204f636418093e34dab66d02442c0beb115a4 GIT binary patch literal 894 zcmV-^1A+XBP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~00001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iyW1 z6%z$tm?4Y+00RC=L_t(Y$F)|^Yg<PU{`B5TPehjWgf9VGPHZc-WrK8ya%gP|P0k6V zJ}8x)`mEfXqhrWF5P`3`xX_D}U}&kJYSX`<4%E=(AjvOkeh`Y{RFdr^Pj7c-duRm0 zl9NNMfe)7bX69pdhTZiQi^bCbG5~D!#~J`O1v6(R;uH3nvlD$$dG@zsJG)LsK(jqE z5(6o(FG3?DF*ucqEr=LOD)iHc7(z3{ZM9&U#z!4Z(?Gpm7f&8P9;{R<k-<pBOC^)_ z__=fMJ|&`=ArujqB$$qO-rL*rudLksYI}QoCKikR<9RKAsZ@?zmgP=OO*#4eonL2X zXAh2xS40rZ3@H_SzTQsdaygXC-MErWCacBbFL#HBhuu=CG$4fFp`jt~`t|QFFD@>9 zd;k8uAI8SUx~kI}U}pI4ZEoH^pT5vjs9vx8mzI{UE-WlOT3%ju+WW0m%djkq0bqZB zUu3h{s{pXIwe=tn2<W3ikGB7Sp@^VJLaJ`8uC9K0@zTY=vWr=#bKfuxZm&&Y(yg^y zzwPesj%|Fu5kHzkk!&9wL3!O%x3jZjTLH_<Udxty@oh6;XnW9ZrBbOUg`W$lxi98k zjgODN?et+L7)lX5>2-lM>$&;4sui?UFTN9@fsmY>oP1fWR?j+5ozPKUsdhz*U>xbu zOd`>oNhF#~1lH){J1unTluArbzm`&p7tfy$wp{nM-|yD|!217+1QA0@dF<4+8~QOG z00NdJ!{KoAz3ZC$2M1<299HdEB8EYvKuST$<MAmzlgTU=i^b`v9c>&Q9Qt#)+&7)E zL<$BGLy`I{tgWs6ST2{(_zWLE{QY4%7!10dJP|_>5u}u#b(f8fjvmg>&;MB{6uu4y zgKzWs{7;=Zi3kQW<0Ozyr(XsF0XG_rHhS?VQV{^e<MGD&`uYZdu6#rUp|u7vpU86% z8W@lOy7FnQA(#lPsITu4f4IFHZ~lE_+mT3rJ`Bw!n7JWX>&;rNmZ{Zhef^yL2c~?N UwP{!X(f|Me07*qoM6N<$f_SKzVE_OC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/images/nav-link_n.png b/Templates/BaseGame/game/tools/navEditor/images/nav-link_n.png new file mode 100644 index 0000000000000000000000000000000000000000..05ef3d74873abba01c7c93ddf90d449179d00ad8 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)m!3HGnb@XfnQjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O?XxI14-?iy0WWg+Z8+Vb&Z8pdfpRr>`sf9VTf}A<N7+IbJ|h7(87ZLp+YZ zy}Z}U*-?P?f&NE!VQKE88OcpIqC4l#?U>p+b?*5G+&6y7HE|sDI&$oo)5edSA_v(6 zf?b+AHcGnaJrsHzC;0KeA*1RUn~l@k>i?AS)J}|dWbU4Eh<jdeU4~5c4p(9I7mozn zXZR`_^(MbbP)gdmVntJJO!6mdrBInfQ(^gkCGy=`vza44Hpb?BdsxiOA0eM2@hYO@ z+MZ`0c1>K~mO5wgT-B+?LVF(a%(c_Xf6S%*Fmqvq6L<Drt583UD+g`e41=az&@?L$ zw=SQcIz@={?2!vwlJ@6qh@IjdtypmLwc%2C@rtf*qJ3F&>>GaWtXjvbX{+^jN`T%y Z#!VUOZhY-l^1$F>@O1TaS?83{1OU~_k)i+q literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/navEditor/main.cs b/Templates/BaseGame/game/tools/navEditor/main.cs new file mode 100644 index 000000000..bcc9cac59 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/main.cs @@ -0,0 +1,274 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 Daniel Buckmaster +// +// 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. +//----------------------------------------------------------------------------- + +// These values should align with enum PolyFlags in walkabout/nav.h +$Nav::WalkFlag = 1 << 0; +$Nav::SwimFlag = 1 << 1; +$Nav::JumpFlag = 1 << 2; +$Nav::LedgeFlag = 1 << 3; +$Nav::DropFlag = 1 << 4; +$Nav::ClimbFlag = 1 << 5; +$Nav::TeleportFlag = 1 << 6; + +function initializeNavEditor() +{ + echo(" % - Initializing Navigation Editor"); + + // Execute all relevant scripts and GUIs. + exec("./navEditor.cs"); + exec("./NavEditorGui.gui"); + exec("./NavEditorToolbar.gui"); + exec("./NavEditorConsoleDlg.gui"); + exec("./CreateNewNavMeshDlg.gui"); + + // Add ourselves to EditorGui, where all the other tools reside + NavEditorGui.setVisible(false); + NavEditorToolbar.setVisible(false); + NavEditorOptionsWindow.setVisible(false); + NavEditorTreeWindow.setVisible(false); + NavEditorConsoleDlg.setVisible(false); + + EditorGui.add(NavEditorGui); + EditorGui.add(NavEditorToolbar); + EditorGui.add(NavEditorOptionsWindow); + EditorGui.add(NavEditorTreeWindow); + EditorGui.add(NavEditorConsoleDlg); + + new ScriptObject(NavEditorPlugin) + { + superClass = "EditorPlugin"; + editorGui = NavEditorGui; + }; + + // Bind shortcuts for the nav editor. + %map = new ActionMap(); + %map.bindCmd(keyboard, "1", "ENavEditorSelectModeBtn.performClick();", ""); + %map.bindCmd(keyboard, "2", "ENavEditorLinkModeBtn.performClick();", ""); + %map.bindCmd(keyboard, "3", "ENavEditorCoverModeBtn.performClick();", ""); + %map.bindCmd(keyboard, "4", "ENavEditorTileModeBtn.performClick();", ""); + %map.bindCmd(keyboard, "5", "ENavEditorTestModeBtn.performClick();", ""); + %map.bindCmd(keyboard, "c", "NavEditorConsoleBtn.performClick();", ""); + NavEditorPlugin.map = %map; + + NavEditorPlugin.initSettings(); +} + +function destroyNavEditor() +{ +} + +function NavEditorPlugin::onWorldEditorStartup(%this) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu("Navigation Editor", "", NavEditorPlugin); + + // Add ourselves to the ToolsToolbar. + %tooltip = "Navigation Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar("NavEditorPlugin", "NavEditorPalette", expandFilename("tools/navEditor/images/nav-editor"), %tooltip); + + GuiWindowCtrl::attach(NavEditorOptionsWindow, NavEditorTreeWindow); + + // Add ourselves to the Editor Settings window. + exec("./NavEditorSettingsTab.gui"); + ESettingsWindow.addTabPage(ENavEditorSettingsPage); + ENavEditorSettingsPage.init(); + + // Add items to World Editor Creator + EWCreatorWindow.beginGroup("Navigation"); + + EWCreatorWindow.registerMissionObject("CoverPoint", "Cover point"); + + EWCreatorWindow.endGroup(); +} + +function ENavEditorSettingsPage::init(%this) +{ + // Initialises the settings controls in the settings dialog box. + %this-->SpawnClassOptions.clear(); + %this-->SpawnClassOptions.add("AIPlayer"); + %this-->SpawnClassOptions.setFirstSelected(); +} + +function NavEditorPlugin::onActivated(%this) +{ + %this.readSettings(); + + // Set a global variable so everyone knows we're editing! + $Nav::EditorOpen = true; + + // Start off in Select mode. + ToolsPaletteArray->NavEditorSelectMode.performClick(); + EditorGui.bringToFront(NavEditorGui); + + NavEditorGui.setVisible(true); + NavEditorGui.makeFirstResponder(true); + NavEditorToolbar.setVisible(true); + + NavEditorOptionsWindow.setVisible(true); + NavEditorTreeWindow.setVisible(true); + + // Inspect the ServerNavMeshSet, which contains all the NavMesh objects + // in the mission. + if(!isObject(ServerNavMeshSet)) + new SimSet(ServerNavMeshSet); + if(ServerNavMeshSet.getCount() == 0) + MessageBoxYesNo("No NavMesh", "There is no NavMesh in this level. Would you like to create one?" SPC + "If not, please use the Nav Editor to create a new NavMesh.", + "Canvas.pushDialog(CreateNewNavMeshDlg);"); + NavTreeView.open(ServerNavMeshSet, true); + + // Push our keybindings to the top. (See initializeNavEditor for where this + // map was created.) + %this.map.push(); + + // Store this on a dynamic field + // in order to restore whatever setting + // the user had before. + %this.prevGizmoAlignment = GlobalGizmoProfile.alignment; + + // Always use Object alignment. + GlobalGizmoProfile.alignment = "Object"; + + // Set the status until some other editing mode adds useful information. + EditorGuiStatusBar.setInfo("Navigation editor."); + EditorGuiStatusBar.setSelection(""); + + // Allow the Gui to setup. + NavEditorGui.onEditorActivated(); + + Parent::onActivated(%this); +} + +function NavEditorPlugin::onDeactivated(%this) +{ + %this.writeSettings(); + + $Nav::EditorOpen = false; + + NavEditorGui.setVisible(false); + NavEditorToolbar.setVisible(false); + NavEditorOptionsWindow.setVisible(false); + NavEditorTreeWindow.setVisible(false); + %this.map.pop(); + + // Restore the previous Gizmo alignment settings. + GlobalGizmoProfile.alignment = %this.prevGizmoAlignment; + + // Allow the Gui to cleanup. + NavEditorGui.onEditorDeactivated(); + + Parent::onDeactivated(%this); +} + +function NavEditorPlugin::onEditMenuSelect(%this, %editMenu) +{ + %hasSelection = false; +} + +function NavEditorPlugin::handleDelete(%this) +{ + // Event happens when the user hits 'delete'. + NavEditorGui.deleteSelected(); +} + +function NavEditorPlugin::handleEscape(%this) +{ + return NavEditorGui.onEscapePressed(); +} + +function NavEditorPlugin::isDirty(%this) +{ + return NavEditorGui.isDirty; +} + +function NavEditorPlugin::onSaveMission(%this, %missionFile) +{ + if(NavEditorGui.isDirty) + { + MissionGroup.save(%missionFile); + NavEditorGui.isDirty = false; + } +} + +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function NavEditorPlugin::initSettings(%this) +{ + EditorSettings.beginGroup("NavEditor", true); + + EditorSettings.setDefaultValue("SpawnClass", "AIPlayer"); + EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData"); + + EditorSettings.endGroup(); +} + +function NavEditorPlugin::readSettings(%this) +{ + EditorSettings.beginGroup("NavEditor", true); + + // Currently these are globals because of the way they are accessed in navMesh.cpp. + $Nav::Editor::renderMesh = EditorSettings.value("RenderMesh"); + $Nav::Editor::renderPortals = EditorSettings.value("RenderPortals"); + $Nav::Editor::renderBVTree = EditorSettings.value("RenderBVTree"); + NavEditorGui.spawnClass = EditorSettings.value("SpawnClass"); + NavEditorGui.spawnDatablock = EditorSettings.value("SpawnDatablock"); + NavEditorGui.backgroundBuild = EditorSettings.value("BackgroundBuild"); + NavEditorGui.saveIntermediates = EditorSettings.value("SaveIntermediates"); + NavEditorGui.playSoundWhenDone = EditorSettings.value("PlaySoundWhenDone"); + + // Build in the background by default, unless a preference has been saved. + if (NavEditorGui.backgroundBuild $= "") + { + NavEditorGui.backgroundBuild = true; + } + + EditorSettings.endGroup(); +} + +function NavEditorPlugin::writeSettings(%this) +{ + EditorSettings.beginGroup("NavEditor", true); + + EditorSettings.setValue("RenderMesh", $Nav::Editor::renderMesh); + EditorSettings.setValue("RenderPortals", $Nav::Editor::renderPortals); + EditorSettings.setValue("RenderBVTree", $Nav::Editor::renderBVTree); + EditorSettings.setValue("SpawnClass", NavEditorGui.spawnClass); + EditorSettings.setValue("SpawnDatablock", NavEditorGui.spawnDatablock); + EditorSettings.setValue("BackgroundBuild", NavEditorGui.backgroundBuild); + EditorSettings.setValue("SaveIntermediates", NavEditorGui.saveIntermediates); + EditorSettings.setValue("PlaySoundWhenDone", NavEditorGui.playSoundWhenDone); + + EditorSettings.endGroup(); +} + +function ESettingsWindowPopup::onWake(%this) +{ + %this.setSelected(%this.findText(EditorSettings.value(%this.editorSettingsValue))); +} + +function ESettingsWindowPopup::onSelect(%this) +{ + EditorSettings.setValue(%this.editorSettingsValue, %this.getText()); + eval(%this.editorSettingsRead); +} diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.cs b/Templates/BaseGame/game/tools/navEditor/navEditor.cs new file mode 100644 index 000000000..b3d5956c0 --- /dev/null +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.cs @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 Daniel Buckmaster +// +// 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. +//----------------------------------------------------------------------------- + +$Nav::EditorOpen = false; + +function NavEditorGui::onEditorActivated(%this) +{ + if(%this.selectedObject) + %this.selectObject(%this.selectedObject); + %this.prepSelectionMode(); +} + +function NavEditorGui::onEditorDeactivated(%this) +{ + if(%this.getMesh()) + %this.deselect(); +} + +function NavEditorGui::onModeSet(%this, %mode) +{ + // Callback when the nav editor changes mode. Set the appropriate dynamic + // GUI contents in the properties/actions boxes. + NavInspector.setVisible(false); + + %actions = NavEditorOptionsWindow->ActionsBox; + %actions->SelectActions.setVisible(false); + %actions->LinkActions.setVisible(false); + %actions->CoverActions.setVisible(false); + %actions->TileActions.setVisible(false); + %actions->TestActions.setVisible(false); + + %properties = NavEditorOptionsWindow->PropertiesBox; + %properties->LinkProperties.setVisible(false); + %properties->TileProperties.setVisible(false); + %properties->TestProperties.setVisible(false); + + switch$(%mode) + { + case "SelectMode": + NavInspector.setVisible(true); + %actions->SelectActions.setVisible(true); + case "LinkMode": + %actions->LinkActions.setVisible(true); + %properties->LinkProperties.setVisible(true); + case "CoverMode": + // + %actions->CoverActions.setVisible(true); + case "TileMode": + %actions->TileActions.setVisible(true); + %properties->TileProperties.setVisible(true); + case "TestMode": + %actions->TestActions.setVisible(true); + %properties->TestProperties.setVisible(true); + } +} + +function NavEditorGui::paletteSync(%this, %mode) +{ + // Synchronise the palette (small buttons on the left) with the actual mode + // the nav editor is in. + %evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);"; + eval(%evalShortcut); +} + +function NavEditorGui::onEscapePressed(%this) +{ + return false; +} + +function NavEditorGui::selectObject(%this, %obj) +{ + NavTreeView.clearSelection(); + if(isObject(%obj)) + NavTreeView.selectItem(%obj); + %this.onObjectSelected(%obj); +} + +function NavEditorGui::onObjectSelected(%this, %obj) +{ + if(isObject(%this.selectedObject)) + %this.deselect(); + %this.selectedObject = %obj; + if(isObject(%obj)) + { + %this.selectMesh(%obj); + NavInspector.inspect(%obj); + } +} + +function NavEditorGui::deleteMesh(%this) +{ + if(isObject(%this.selectedObject)) + { + %this.selectedObject.delete(); + %this.selectObject(-1); + } +} + +function NavEditorGui::deleteSelected(%this) +{ + switch$(%this.getMode()) + { + case "SelectMode": + // Try to delete the selected NavMesh. + if(isObject(NavEditorGui.selectedObject)) + MessageBoxYesNo("Warning", + "Are you sure you want to delete" SPC NavEditorGui.selectedObject.getName(), + "NavEditorGui.deleteMesh();"); + case "TestMode": + %this.getPlayer().delete(); + %this.onPlayerDeselected(); + case "LinkMode": + %this.deleteLink(); + %this.isDirty = true; + } +} + +function NavEditorGui::buildSelectedMeshes(%this) +{ + if(isObject(%this.getMesh())) + { + %this.getMesh().build(NavEditorGui.backgroundBuild, NavEditorGui.saveIntermediates); + %this.isDirty = true; + } +} + +function NavEditorGui::buildLinks(%this) +{ + if(isObject(%this.getMesh())) + { + %this.getMesh().buildLinks(); + %this.isDirty = true; + } +} + +function updateLinkData(%control, %flags) +{ + %control->LinkWalkFlag.setActive(true); + %control->LinkJumpFlag.setActive(true); + %control->LinkDropFlag.setActive(true); + %control->LinkLedgeFlag.setActive(true); + %control->LinkClimbFlag.setActive(true); + %control->LinkTeleportFlag.setActive(true); + + %control->LinkWalkFlag.setStateOn(%flags & $Nav::WalkFlag); + %control->LinkJumpFlag.setStateOn(%flags & $Nav::JumpFlag); + %control->LinkDropFlag.setStateOn(%flags & $Nav::DropFlag); + %control->LinkLedgeFlag.setStateOn(%flags & $Nav::LedgeFlag); + %control->LinkClimbFlag.setStateOn(%flags & $Nav::ClimbFlag); + %control->LinkTeleportFlag.setStateOn(%flags & $Nav::TeleportFlag); +} + +function getLinkFlags(%control) +{ + return (%control->LinkWalkFlag.isStateOn() ? $Nav::WalkFlag : 0) | + (%control->LinkJumpFlag.isStateOn() ? $Nav::JumpFlag : 0) | + (%control->LinkDropFlag.isStateOn() ? $Nav::DropFlag : 0) | + (%control->LinkLedgeFlag.isStateOn() ? $Nav::LedgeFlag : 0) | + (%control->LinkClimbFlag.isStateOn() ? $Nav::ClimbFlag : 0) | + (%control->LinkTeleportFlag.isStateOn() ? $Nav::TeleportFlag : 0); +} + +function disableLinkData(%control) +{ + %control->LinkWalkFlag.setActive(false); + %control->LinkJumpFlag.setActive(false); + %control->LinkDropFlag.setActive(false); + %control->LinkLedgeFlag.setActive(false); + %control->LinkClimbFlag.setActive(false); + %control->LinkTeleportFlag.setActive(false); +} + +function NavEditorGui::onLinkSelected(%this, %flags) +{ + updateLinkData(NavEditorOptionsWindow-->LinkProperties, %flags); +} + +function NavEditorGui::onPlayerSelected(%this, %flags) +{ + updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags); +} + +function NavEditorGui::updateLinkFlags(%this) +{ + if(isObject(%this.getMesh())) + { + %properties = NavEditorOptionsWindow-->LinkProperties; + %this.setLinkFlags(getLinkFlags(%properties)); + %this.isDirty = true; + } +} + +function NavEditorGui::updateTestFlags(%this) +{ + if(isObject(%this.getPlayer())) + { + %properties = NavEditorOptionsWindow-->TestProperties; + %player = %this.getPlayer(); + + %player.allowWwalk = %properties->LinkWalkFlag.isStateOn(); + %player.allowJump = %properties->LinkJumpFlag.isStateOn(); + %player.allowDrop = %properties->LinkDropFlag.isStateOn(); + %player.allowLedge = %properties->LinkLedgeFlag.isStateOn(); + %player.allowClimb = %properties->LinkClimbFlag.isStateOn(); + %player.allowTeleport = %properties->LinkTeleportFlag.isStateOn(); + + %this.isDirty = true; + } +} + +function NavEditorGui::onLinkDeselected(%this) +{ + disableLinkData(NavEditorOptionsWindow-->LinkProperties); +} + +function NavEditorGui::onPlayerDeselected(%this) +{ + disableLinkData(NavEditorOptionsWindow-->TestProperties); +} + +function NavEditorGui::createCoverPoints(%this) +{ + if(isObject(%this.getMesh())) + { + %this.getMesh().createCoverPoints(); + %this.isDirty = true; + } +} + +function NavEditorGui::deleteCoverPoints(%this) +{ + if(isObject(%this.getMesh())) + { + %this.getMesh().deleteCoverPoints(); + %this.isDirty = true; + } +} + +function NavEditorGui::findCover(%this) +{ + if(%this.getMode() $= "TestMode" && isObject(%this.getPlayer())) + { + %pos = LocalClientConnection.getControlObject().getPosition(); + %text = NavEditorOptionsWindow-->TestProperties->CoverPosition.getText(); + if(%text !$= "") + %pos = eval(%text); + %this.getPlayer().findCover(%pos, NavEditorOptionsWindow-->TestProperties->CoverRadius.getText()); + } +} + +function NavEditorGui::followObject(%this) +{ + if(%this.getMode() $= "TestMode" && isObject(%this.getPlayer())) + { + %obj = LocalClientConnection.player; + %text = NavEditorOptionsWindow-->TestProperties->FollowObject.getText(); + if(%text !$= "") + { + eval("%obj = " @ %text); + if(!isObject(%obj)) + MessageBoxOk("Error", "Cannot find object" SPC %text); + } + if(isObject(%obj)) + %this.getPlayer().followObject(%obj, NavEditorOptionsWindow-->TestProperties->FollowRadius.getText()); + } +} + +function NavInspector::inspect(%this, %obj) +{ + %name = ""; + if(isObject(%obj)) + %name = %obj.getName(); + else + NavFieldInfoControl.setText(""); + + Parent::inspect(%this, %obj); +} + +function NavInspector::onInspectorFieldModified(%this, %object, %fieldName, %arrayIndex, %oldValue, %newValue) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified(%this, %object, %fieldName, %arrayIndex, %oldValue, %newValue); +} + +function NavInspector::onFieldSelected(%this, %fieldName, %fieldTypeStr, %fieldDoc) +{ + NavFieldInfoControl.setText("<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc); +} + +function NavTreeView::onInspect(%this, %obj) +{ + NavInspector.inspect(%obj); +} + +function NavTreeView::onSelect(%this, %obj) +{ + NavInspector.inspect(%obj); + NavEditorGui.onObjectSelected(%obj); +} + +function NavEditorGui::prepSelectionMode(%this) +{ + %this.setMode("SelectMode"); + ToolsPaletteArray-->NavEditorSelectMode.setStateOn(1); +} + +//----------------------------------------------------------------------------- + +function ENavEditorPaletteButton::onClick(%this) +{ + // When clicking on a pelette button, add its description to the bottom of + // the editor window. + EditorGuiStatusBar.setInfo(%this.DetailedDesc); +} + +//----------------------------------------------------------------------------- + +function NavMeshLinkFlagButton::onClick(%this) +{ + NavEditorGui.updateLinkFlags(); +} + +function NavMeshTestFlagButton::onClick(%this) +{ + NavEditorGui.updateTestFlags(); +} + +singleton GuiControlProfile(NavEditorProfile) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192 192"; + category = "Editor"; +}; + +singleton GuiControlProfile(GuiSimpleBorderProfile) +{ + opaque = false; + border = 1; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/particleEditor/ParticleEditor.ed.gui b/Templates/BaseGame/game/tools/particleEditor/ParticleEditor.ed.gui new file mode 100644 index 000000000..a2c87457e --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/ParticleEditor.ed.gui @@ -0,0 +1,3324 @@ +//----------------------------------------------------------------------------- +// Torque +// Copyright GarageGames, LLC 2011 +//----------------------------------------------------------------------------- + +$PE_guielement_pos_single_container = "0 0"; +$PE_guielement_ext_single_container = "184 20"; +$PE_guielement_pos_name = "1 0"; +$PE_guielement_ext_name = "70 18"; +$PE_guielement_pos_slider = "74 2"; +$PE_guielement_ext_slider = "58 12"; +$PE_guielement_pos_value = "138 0"; +$PE_guielement_ext_value = "36 18"; +$PE_guielement_pos_textedit = "74 0"; +$PE_guielement_ext_textedit = "100 18"; +$PE_guielement_ext_checkbox_name = "156 18"; +$PE_guielement_pos_checkbox = "161 0"; +$PE_guielement_ext_checkbox = "15 18"; +$PE_guielement_pos_colorpicker = "158 0"; +$PE_guielement_ext_colorpicker = "18 18"; + +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCollapseCtrl(PE_Window) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + Position = firstWord($pref::Video::mode) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) -1; + Extent = "210 696"; + MinExtent = "210 140"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = ""; + EdgeSnap = "0"; + text = "Particle Editor"; + + new GuiTabBookCtrl(PE_TabBook) { + canSaveDynamicFields = "0"; + isContainer = "1"; + internalName = "EditorTabBook"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "6 27"; + Extent = "197 289"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "3 2 3 3"; + Docking = "client"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "0"; + MinTabWidth = "64"; + + new GuiTabPageCtrl(PE_EmitterEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "197 271"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Emitter"; + maxLength = "1024"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "197 271"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(PEE_EmitterSelector_Control){ // PEE_EmitterSelector + class = "QuickEditDropDownTextEditCtrl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = "197 26" ; + + new GuiPopUpMenuCtrl(PEE_EmitterSelector) { + internalName = "PopUpMenu"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 4"; + Extent = "123 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor::onNewEmitter();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "255"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiTextEditCtrl() { + internalName = "TextEdit"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiDropdownTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 4"; + Extent = "107 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + AltCommand = "$ThisControl.getParent().updateFromChild($ThisControl);"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "131 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.showNewDialog();"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + tooltip = "Create New Emitter"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "147 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/save-icon"; + command = "PE_EmitterEditor.saveEmitter( " @ PE_EmitterEditor.currEmitter @ " ); PE_ParticleEditor.saveParticle( PE_ParticleEditor.currParticle );"; + tooltip = "Save Current Emitter"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "164 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.showDeleteDialog();"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + tooltip = "Delete Current Emitter"; + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Basic"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Emitter PEE_lifetimeMS + isContainer = "1"; + class = "AggregateControl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Life"; + maxLength = "255"; + }; + new GuiSliderCtrl(PEE_lifetimeMS) { + internalName = "PEE_lifetimeMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateLifeFields( false, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateLifeFields( false, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 1000"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_lifetimeMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateLifeFields( false, $ThisControl.getValue() );"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + }; + }; + + new GuiControl(){ // Emitter PEE_lifetimeVarianceMS + isContainer = "1"; + class = "AggregateControl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Life Random"; + maxLength = "255"; + }; + new GuiSliderCtrl(PEE_lifetimeVarianceMS) { + internalName = "PEE_lifetimeVarianceMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateLifeFields( true, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateLifeFields( true, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 1000"; + ticks = "0"; + value = ""; + }; + new GuiTextEditCtrl() { + internalName = "PEE_lifetimeVarianceMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateLifeFields( true, $ThisControl.getValue() );"; + }; + }; + + new GuiControl(){ // Emitter Infinite Loop + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Infinite Loop"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_infiniteLoop"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + Command = "PE_EmitterEditor.updateLifeFieldsInfiniteLoop();"; + text = ""; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Emitter Amount + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Amount"; + }; + new GuiSliderCtrl(PEE_ejectionPeriodMS) { + internalName = "PEE_ejectionPeriodMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateAmountFields( false, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateAmountFields( false, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "1 1000"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_ejectionPeriodMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateAmountFields( false, $ThisControl.getValue() );"; + }; + }; + new GuiControl(){ // Emitter Amount Random + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Amount Random"; + }; + new GuiSliderCtrl(PEE_periodVarianceMS) { + internalName = "PEE_periodVarianceMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateAmountFields( true, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateAmountFields( true, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 999"; + ticks = "0"; + value = "1"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_periodVarianceMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateAmountFields( true, $ThisControl.getValue() );"; + }; + }; + + new GuiControl(){ // Particle glow + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Glow"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_glow"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + Command = "PE_EmitterEditor.updateEmitter( \"glow\", $ThisControl.getValue());"; + text = ""; + }; + }; + };// end stack + }; // end "basic" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Motion"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Emitter speed + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Speed"; + }; + new GuiSliderCtrl(PEE_ejectionVelocity) { + internalName = "PEE_ejectionVelocity_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateSpeedFields( false, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateSpeedFields( false, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 100"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_ejectionVelocity_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateSpeedFields( false, $ThisControl.getValue() );"; + }; + }; + new GuiControl(){ // Emitter speed random + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Speed Random"; + }; + new GuiSliderCtrl(PEE_velocityVariance) { + internalName = "PEE_velocityVariance_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateSpeedFields( true, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateSpeedFields( true, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 100"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_velocityVariance_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateSpeedFields( true, $ThisControl.getValue() );"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Emitter Orient to Movment Direction + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Orient to Movment Direction"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_orientParticles"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"orientParticles\", $ThisControl.getValue());"; + }; + }; + new GuiControl(){ // Emitter Align to a Direction + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Align to a Direction"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_alignParticles"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"alignParticles\", $ThisControl.getValue());"; + }; + }; + new GuiControl(){ // Emitter Align Direction + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Align Direction"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_alignDirection"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + text = "0 0 0"; + altCommand = "PE_EmitterEditor.updateEmitter( \"alignDirection\", $ThisControl.getText());"; + }; + }; + }; // end stack + }; // end "motion" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Spread"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Emitter Angle Min + isContainer = "1"; + class = "AggregateControl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiBitmapCtrl(){ // 0 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+4 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "0 Degrees ( Up )"; + }; + new GuiBitmapCtrl(){ // 90 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/4)+1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "90 Degrees ( Left )"; + }; + new GuiBitmapCtrl(){ // 180 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2)-1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "180 Degrees ( Down )"; + }; + new GuiBitmapCtrl(){ // 270 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2+mCeil(getWord($PE_guielement_ext_slider,0)/4))-4 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "270 Degrees ( Right )"; + }; + new GuiBitmapCtrl(){ // 360 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0))-5 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "360 Degrees ( Up )"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Angle Min"; + }; + new GuiSliderCtrl(PEE_thetaMin) { + internalName = "PEE_thetaMin_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateEmitter( \"thetaMin\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"thetaMin\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 180"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalname = "PEE_thetaMin_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"thetaMin\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Emitter Angle Max + isContainer = "1"; + class = "AggregateControl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiBitmapCtrl(){ // 0 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+4 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "0 Degrees ( Up )"; + }; + new GuiBitmapCtrl(){ // 90 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/4)+1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "90 Degrees ( Left )"; + }; + new GuiBitmapCtrl(){ // 180 Degrees + HorizSizing = "left"; + minExtent = "0 0"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2)-1 SPC "0"; + Extent = "2 18"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "180 Degrees ( Down )"; + }; + new GuiBitmapCtrl(){ // 270 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2+mCeil(getWord($PE_guielement_ext_slider,0)/4))-4 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "270 Degrees ( Right )"; + }; + new GuiBitmapCtrl(){ // 360 Degrees + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0))-5 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "360 Degrees ( Up )"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Angle Max"; + }; + new GuiSliderCtrl(PEE_thetaMax) { + internalName = "PEE_thetaMax_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateEmitter( \"thetaMax\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"thetaMax\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 180"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_thetaMax_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"thetaMax\", $ThisControl.getText());"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Emitter Depth + isContainer = "1"; + class = "AggregateControl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Depth"; + }; + new GuiSliderCtrl(PEE_phiVariance) { + internalName = "PEE_phiVariance_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateEmitter( \"phiVariance\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"phiVariance\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 360"; + ticks = "0"; + value = "360"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_phiVariance_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"phiVariance\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Emitter Offset + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Offset"; + }; + new GuiSliderCtrl(PEE_ejectionOffset) { + internalName = "PEE_ejectionOffset_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateEmitter( \"ejectionOffset\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ejectionOffset\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 10"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_ejectionOffset_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ejectionOffset\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Emitter Offset Variance + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "OffsetVariance"; + }; + new GuiSliderCtrl(PEE_ejectionOffsetVariance) { + internalName = "PEE_ejectionOffsetVariance_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_EmitterEditor.updateEmitter( \"ejectionOffsetVariance\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ejectionOffsetVariance\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 25"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_ejectionOffsetVariance_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ejectionOffsetVariance\", $ThisControl.getText());"; + }; + }; + }; // end stack + }; // end "spread" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Particles"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(PEE_EmitterParticle1){ // emmiter particle 1 + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Particle 1"; + }; + new GuiPopUpMenuCtrl(PEE_EmitterParticleSelector1) { + internalName = "PopUpMenu"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + text = ""; + command = "PE_EmitterEditor.updateParticlesFields($ThisControl);"; + }; + }; + new GuiControl(PEE_EmitterParticle2){ // emmiter particle 2 + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Particle 2"; + }; + new GuiPopUpMenuCtrl(PEE_EmitterParticleSelector2) { + internalName = "PopUpMenu"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + text = "None"; + command = "PE_EmitterEditor.updateParticlesFields();"; + }; + // Clear particle 2 + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "56 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "%particleId = PEE_EmitterParticle2-->PopUpMenu.findText( \"None\" ); PEE_EmitterParticle2-->PopUpMenu.setSelected( %particleId );PE_EmitterEditor.updateParticlesFields();"; + hovertime = "1000"; + tooltip = "Clear Particle 2 from Emitter"; + text = ""; + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiControl(PEE_EmitterParticle3){ // emmiter particle 3 + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Particle 3"; + }; + new GuiPopUpMenuCtrl(PEE_EmitterParticleSelector3) { + internalName = "PopUpMenu"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + text = "None"; + command = "PE_EmitterEditor.updateParticlesFields();"; + }; + // Clear particle 3 + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "56 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "%particleId = PEE_EmitterParticle3-->PopUpMenu.findText( \"None\" ); PEE_EmitterParticle3-->PopUpMenu.setSelected( %particleId );PE_EmitterEditor.updateParticlesFields();"; + hovertime = "1000"; + tooltip = "Clear Particle 3 from Emitter"; + text = ""; + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiControl(PEE_EmitterParticle4){ // emmiter particle 4 + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Particle 4"; + }; + new GuiPopUpMenuCtrl(PEE_EmitterParticleSelector4) { + internalName = "PopUpMenu"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + text = "None"; + command = "PE_EmitterEditor.updateParticlesFields();"; + }; + // Clear particle 4 + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "56 1"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "%particleId = PEE_EmitterParticle4-->PopUpMenu.findText( \"None\" ); PEE_EmitterParticle4-->PopUpMenu.setSelected( %particleId );PE_EmitterEditor.updateParticlesFields();"; + hovertime = "1000"; + tooltip = "Clear Particle 4 from Emitter"; + text = ""; + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + }; // end stack + }; // end "particles" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Blending"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Blend type + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Blend Type"; + }; + new GuiPopUpMenuCtrl() { + internalName = "PEE_blendType"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + command = "PE_EmitterEditor.updateEmitter( \"blendStyle\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // softness Distance + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Softness Distance "; + }; + new GuiSliderCtrl() { + internalName = "PEE_softnessDistance_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 1000"; + ticks = "0"; + value = "0"; + Command = "PE_EmitterEditor.updateEmitter( \"softnessDistance\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"softnessDistance\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_softnessDistance_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"softnessDistance\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Ambient Factor + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Ambient Factor"; + }; + new GuiSliderCtrl() { + internalName = "PEE_ambientFactor_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 10"; + ticks = "0"; + value = "0"; + Command = "PE_EmitterEditor.updateEmitter( \"ambientFactor\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ambientFactor\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEE_ambientFactor_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_EmitterEditor.updateEmitter( \"ambientFactor\", $ThisControl.getText));"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Sort Particles + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Sort Particles"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_softParticles"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"softParticles\", $ThisControl.getValue());"; + }; + }; + new GuiControl(){ // Emitter Reverse Order + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Reverse Order"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEE_reverseOrder"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"reverseOrder\", $ThisControl.getValue());"; + }; + }; + new GuiControl(){ // Emitter Use Emitter Size + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Use Emitter Size"; + }; + new GuiCheckBoxCtrl(PEE_useEmitterSizes) { + internalName = "PEE_useEmitterSizes"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"useEmitterSizes\", $ThisControl.getValue());"; + }; + }; + new GuiControl(){ // Emitter use Material Effect Color + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_checkbox_name; + text = "Use Material Effect Color"; + }; + new GuiCheckBoxCtrl(PEE_useEmitterColors) { + internalName = "PEE_useEmitterColors"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_checkbox; + Extent = $PE_guielement_ext_checkbox; + text = ""; + command = "PE_EmitterEditor.updateEmitter( \"useEmitterColors\", $ThisControl.getValue());"; + }; + }; + }; // end stack + }; // end "Blending" rollout + };// end stack "Emitter" + };// end scroll "Emitter" + };// end tab page "Emitter" + new GuiTabPageCtrl(PE_ParticleEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "197 271"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Particle"; + maxLength = "1024"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "197 271"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(PEP_ParticleSelector_Control){ // PEP_ParticleSelector + isContainer = "1"; + class = "QuickEditDropDownTextEditCtrl"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = "197 26" ; + + new GuiPopUpMenuCtrl(PEP_ParticleSelector) { + internalName = "PopUpMenu"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 4"; + Extent = "123 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.onNewParticle();"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "255"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiTextEditCtrl() { + internalName = "TextEdit"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiDropdownTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 4"; + Extent = "107 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl);"; + }; + new GuiBitmapButtonCtrl( PEP_NewParticleButton ) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "131 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + tooltip = "Add New Particle To Current Emitter"; + useModifiers = "1"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "147 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/save-icon"; + command = "PE_ParticleEditor.saveParticle( PE_ParticleEditor.currParticle );"; + tooltip = "Save Current Particle"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "164 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.showDeleteDialog();"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + tooltip = "Delete Current Particle"; + }; + }; + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Basic"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // particle texture map + profile="ToolsGuiDefaultProfile"; + isContainer = "1"; + position = "0 0"; + Extent = "185 52"; + HorizSizing = "width"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -2"; + Extent = "72 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Texture Map"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + internalName = "PEP_previewImage"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Edit Selected Particle."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Edit Selected Particle."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + Command = "PE_ParticleEditor.updateParticleTexture(1);"; + }; + new GuiCheckBoxCtrl() { + internalName = "PEP_inverseAlpha"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "55 14"; + Extent = "84 18"; + MinExtent = "8 2"; + text = "Inverse Alpha"; + command = "PE_ParticleEditor.updateParticle( \"useInvAlpha\", $ThisControl.getValue());"; + }; + new GuiTextEditCtrl(PEP_textureName) { + internalName = "PEP_previewImageName"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "55 31"; + Extent = "120 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl(){ + profile="ToolsGuiButtonProfile"; + text ="Edit"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "138 0"; + Extent = "36 18" ; + buttonType = "PushButton"; + Command = "PE_ParticleEditor.updateParticleTexture(1);"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + //visible = false; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // particle life + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Life"; + maxLength = "255"; + }; + new GuiSliderCtrl(PEP_lifetimeMS) { + internalName = "PEP_lifetimeMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateLifeFields( false, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateLifeFields( false, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "1 9000"; + ticks = "0"; + value = "3000"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_lifetimeMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateLifeFields( false, $ThisControl.getText() );"; + }; + }; + new GuiControl(){ // particle life Random + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Life Random"; + maxLength = "255"; + }; + new GuiSliderCtrl(PEP_lifetimeVarianceMS) { + internalName = "PEP_lifetimeVarianceMS_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateLifeFields( true, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateLifeFields( true, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 8999"; + ticks = "0"; + value = "3000"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_lifetimeVarianceMS_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateLifeFields( true, $ThisControl.getText() );"; + }; + }; + }; // end stack + }; // end "Particles Basic" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Motion"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Particle Initial speed + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Initial Speed"; + }; + new GuiSliderCtrl(PEP_inheritedVelFactor) { + internalName = "PEP_inheritedVelFactor_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateParticle( \"inheritedVelFactor\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"inheritedVelFactor\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 10"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_inheritedVelFactor_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"inheritedVelFactor\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Acceleration + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Acceleration"; + }; + new GuiSliderCtrl(PEP_constantAcceleration) { + internalName = "PEP_constantAcceleration_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateParticle( \"constantAcceleration\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"constantAcceleration\", $ThisControl.getValue(), true, false);"; + hovertime = "1000"; + range = "-10 10"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_constantAcceleration_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"constantAcceleration\", $ThisControl.getText());"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Particle Gravity + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiBitmapCtrl(){ // 0 Gravity + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2)-1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "0 Gravity"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Gravity"; + }; + new GuiSliderCtrl(PEP_gravityCoefficient) { + internalName = "PEP_gravityCoefficient_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateParticle( \"gravityCoefficient\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"gravityCoefficient\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "-1 1"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_gravityCoefficient_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"gravityCoefficient\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Drag + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Drag"; + }; + new GuiSliderCtrl(PEP_dragCoefficient) { + internalName = "PEP_dragCoefficient_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateParticle( \"dragCoefficient\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"dragCoefficient\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0.298143"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_dragCoefficient_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"dragCoefficient\", $ThisControl.getText());"; + }; + }; //End Particle Drag + new GuiControl(){ // Particle Wind + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Wind Coeff"; + }; + new GuiSliderCtrl(PEP_windCoefficient) { + internalName = "PEP_windCoefficient_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateParticle( \"windCoefficient\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"windCoefficient\", $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0.298143"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_windCoefficient_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"windCoefficient\", $ThisControl.getText());"; + }; + }; + }; // end stack + }; // end "motion" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Spin"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Particle spin Min + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiBitmapCtrl(){ // No Spin + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2)-1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "No Spin"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Spin Min"; + }; + new GuiSliderCtrl(PEP_spinRandomMin) { + internalName = "PEP_spinRandomMin_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateSpinFields( true, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateSpinFields( true, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "-1000 999"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_spinRandomMin_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateSpinFields( true, $ThisControl.getText() );"; + }; + }; + new GuiControl(){ // Particle Spin Max + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiBitmapCtrl(){ // No Spin + HorizSizing = "left"; + position = getWord($PE_guielement_pos_slider,0)+mCeil(getWord($PE_guielement_ext_slider,0)/2)-1 SPC "0"; + Extent = "2 18"; + minExtent = "0 0"; + bitmap = "tools/gui/images/separator-h"; + tooltip = "No Spin"; + }; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Spin Max"; + }; + new GuiSliderCtrl(PEP_spinRandomMax) { + internalName = "PEP_spinRandomMax_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PE_ParticleEditor.updateSpinFields( false, $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateSpinFields( false, $ThisControl.getValue(), true, false );"; + hovertime = "1000"; + range = "-999 1000"; + ticks = "0"; + value = "0"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_spinRandomMax_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateSpinFields( false, $ThisControl.getText() );"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Particle spin Speed + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Spin Speed"; + }; + new GuiSliderCtrl(PEP_spinSpeed) { + internalName = "PEP_spinSpeed_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"spinSpeed\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"spinSpeed\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_spinSpeed_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"spinSpeed\", $ThisControl.getText());"; + }; + }; + }; // end stack + }; // end "Spin" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Animation"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiCheckBoxCtrl() { + internalName = "PEP_animateTexture"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "55 14"; + Extent = "84 18"; + MinExtent = "8 2"; + text = "Animate Texture"; + command = "PE_ParticleEditor.updateParticle( \"animateTexture\", $ThisControl.getValue());"; + }; + new GuiControl(){ // Particle framesPerSec + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "framesPerSec"; + }; + new GuiSliderCtrl(PEP_framesPerSec) { + internalName = "PEP_framesPerSec_slider"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + range = "0 60"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"framesPerSec\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"framesPerSec\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_framesPerSec_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"framesPerSec\", $ThisControl.getText());"; + }; + }; // end framesPerSec + new GuiControl(){ // Particle animTexFramesList + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container; + Extent = $PE_guielement_ext_single_container; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "animTexFrames"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_animTexFramesList_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_textedit; + Extent = $PE_guielement_ext_textedit; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"animTexFrames\", $ThisControl.getText());"; + }; + }; // end animTexFramesList + new GuiControl(){ // Particle animTileCount + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container; + Extent = $PE_guielement_ext_single_container; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "TileCount (X Y)"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_animTileCount_textEdit"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"animTexTiling\", $ThisControl.getText());"; + }; + }; // end animTileCount + }; // end stack + }; // end "Anim" rollout + new GuiRolloutCtrl() { + class = "BehaviorQuickEditRollout"; + superclass = LBQuickEditRollout; + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "197 0"; + Caption = "Overtime"; + Margin = "4 4 4 0"; + DragSizable = false; + container = true; + parentRollout = %this.rollout; + object = %behavior; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "197 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl(){ // Particle Point Colors + class = ""; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Colors"; + }; + new GuiSwatchButtonCtrl(PE_ColorTintSwatch0) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "75 0"; + Extent = $PE_guielement_ext_colorpicker; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF( PE_ParticleEditor.currParticle.colors[0], \"PE_ColorTintSwatch0.updateParticleColor\");"; + altCommand = "$ThisControl.updateParticleColor( $ThisControl.color );"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + arrayNum = "0"; + class = "PE_ColorTintSwatch"; + }; + new GuiSwatchButtonCtrl(PE_ColorTintSwatch1) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "102 0"; + Extent = $PE_guielement_ext_colorpicker; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF( PE_ParticleEditor.currParticle.colors[1], \"PE_ColorTintSwatch1.updateParticleColor\");"; + altCommand = "$ThisControl.updateParticleColor( $ThisControl.color );"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + arrayNum = "1"; + class = "PE_ColorTintSwatch"; + }; + new GuiSwatchButtonCtrl(PE_ColorTintSwatch2) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "129 0"; + Extent = $PE_guielement_ext_colorpicker; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF( PE_ParticleEditor.currParticle.colors[2], \"PE_ColorTintSwatch2.updateParticleColor\");"; + altCommand = "$ThisControl.updateParticleColor( $ThisControl.color );"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + arrayNum = "2"; + class = "PE_ColorTintSwatch"; + }; + new GuiSwatchButtonCtrl(PE_ColorTintSwatch3) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "156 0"; + Extent = $PE_guielement_ext_colorpicker; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "getColorF( PE_ParticleEditor.currParticle.colors[3], \"PE_ColorTintSwatch3.updateParticleColor\");"; + altCommand = "$ThisControl.updateParticleColor( $ThisControl.color );"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + arrayNum = "3"; + class = "PE_ColorTintSwatch"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Particle Point Size + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Size 1"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointSize_slider0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 50"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"sizes[0]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[0]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointSize_textEdit0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[0]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Size + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Size 2"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointSize_slider1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 50"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"sizes[1]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[1]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointSize_textEdit1"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[1]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Size + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Size 3"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointSize_slider2"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 50"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"sizes[2]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[2]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointSize_textEdit2"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[2]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Size + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Size 4"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointSize_slider3"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 50"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"sizes[3]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[3]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointSize_textEdit3"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"sizes[3]\", $ThisControl.getText());"; + }; + }; + + new GuiControl(){ // Spacer ---------------------------- + isContainer = "1"; HorizSizing = "width"; Position = "0 0"; Extent = "194 8"; + new GuiBitmapCtrl(){ + position="0 3"; extent ="188 2"; HorizSizing = "width"; + bitmap ="tools/gui/images/separator-v"; + }; + };// end spacer ---------------------------------------- + + new GuiControl(){ // Particle Point Time + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Time 1"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointTime_slider0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"times[0]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[0]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointTime_textEdit0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.setText(mClamp( $ThisControl.getValue(), 0.0, 1.0)); $ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[0]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Time + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Time 2"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointTime_slider1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"times[1]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[1]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointTime_textEdit1"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.setText(mClamp( $ThisControl.getValue(), 0.0, 1.0)); $ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[1]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Time + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Time 3"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointTime_slider2"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"times[2]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[2]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointTime_textEdit2"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.setText(mClamp( $ThisControl.getValue(), 0.0, 1.0)); $ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[2]\", $ThisControl.getText());"; + }; + }; + new GuiControl(){ // Particle Point Time + class = "AggregateControl"; + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = $PE_guielement_pos_single_container ; + Extent = $PE_guielement_ext_single_container ; + + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = $PE_guielement_pos_name; + Extent = $PE_guielement_ext_name; + text = "Time 4"; + }; + new GuiSliderCtrl() { + internalName = "PEP_pointTime_slider3"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_slider; + Extent = $PE_guielement_ext_slider; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + hovertime = "1000"; + range = "0 1"; + ticks = "0"; + value = "0"; + Command = "PE_ParticleEditor.updateParticle( \"times[3]\", $ThisControl.getValue(), true, true );"; + altCommand = "$ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[3]\", $ThisControl.getValue(), true, false );"; + }; + new GuiTextEditCtrl() { + internalName = "PEP_pointTime_textEdit3"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = $PE_guielement_pos_value; + Extent = $PE_guielement_ext_value; + altCommand = "$ThisControl.setText(mClamp( $ThisControl.getValue(), 0.0, 1.0)); $ThisControl.getParent().updateFromChild($ThisControl); PE_ParticleEditor.updateParticle( \"times[3]\", $ThisControl.getText());"; + }; + }; + }; // end stack + }; // end "Overtime" rollout + };// end stack "Particles" + };// end scroll "Particles" + };// end tab page "Particles" + };// end tab book + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "169 25"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ParticleEditor.updateEmitterNode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play Particle Effect from Start"; + hovertime = "1000"; + bitmap = "tools/particleEditor/images/play_btn"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "189 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ParticleEditor.resetEmitterNode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Drops Particle Effect in front of the Camera"; + hovertime = "1000"; + bitmap = "tools/classIcons/camera"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; +};// end window +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/particleEditor/images/play_btn_d.png b/Templates/BaseGame/game/tools/particleEditor/images/play_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..518a15685900a97339eb6592015f43750dc96a90 GIT binary patch literal 450 zcmV;z0X_bSP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzWJyFpRCwBA{Qv(y12q5>1C_wwGl&J3 zgr1?<d0_Y@UAcJX0!&QT!1O=KE@EV4GGJn2KBsG7HbGfcO9Z6$@`clkKye1F7+0(r znph)CJo@wJciEMTr`G}5e}VYFfw9$pqFltp%#4iX*w{H%7@FFw)6_9i1xhntID3o< zSq)AXnOZvG3XhL}|Nd<|fBMh_sO$cjSUCR2X^^>%8{U-q@892PXO8bH0WyDs3}nIW zHC$5vKYjo9wdvxS!%Ki1Hjn@a<1~mFi$TAC|5|tV=B3uRub;n%CLLyII5QIEBB1x5 zynFMa^4i4{CqTXe;y+OQ7s|)&B4Ct2T=DB?@38}$r$TN10}Yd3NEjsc52rzlOw0#A zzJHx{{^Y*5(6C^DYW#_00N6zi9^trS4VZ*kpayV2!{Q&3EB?X~Cc(T0RrD9?0!AqN s50U}@iORG{f%*@L{}0^&7yt+`0Ew5I^J__issI2007*qoM6N<$f+iNrP5=M^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/particleEditor/images/play_btn_h.png b/Templates/BaseGame/game/tools/particleEditor/images/play_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..87543b362208de1036963cb2f0db9c8b56bbfc50 GIT binary patch literal 492 zcmV<I0Tcd-P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzj!8s8RCwBA{Qv(y12q5>1C;>P3}V41 zSl_$+{J+0{lUmASFTlj=dY1hs*+qZ;{4x0V|Nps)w)qoc)2l^5>Y7TT7=hvpxG*EO zz%Fl@3l{{+Kl=XdOWBl;!gWCIUyx8)^PK-gx#-ugA8`Ep_fI(%R@N1{RTJ06q*bZ{ zrJ1{GQ<#v|fG}1UWtaEj2#^0CfB*W`HhV(#1gPu&WtH{($LXS<KYrlAKl%Ut``;O# zRo@R(#s+fTw5}3HkQ`PQ#b(rCbrDeQr%xZ=Hf>%ta|w|917s%<{|31XYxMv6`2&k9 zzpp!ce0S@UhqvEDlMOS}M~pbr(N83U{{R2?<mI!6l_w8vI{~s8lscgJFO>fur$N7e z{eZgcd+(kt%ceqY{sZ;KFC+{S`{xy${2zxwzYe~C_bThm(VcIhfyw~Y_!G$hu!~%M zWB&uyV-IXbMn)E>0UXe<_=n_*zp#W!Ft0%s{e`-K5z799WWax-GA&Y|{zKyb!)5?6 iF8YUL5W&(9Aix0m#sgc43D<Q10000<MNUMnLSTX$=Il8D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/particleEditor/images/play_btn_n.png b/Templates/BaseGame/game/tools/particleEditor/images/play_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..7fadf843dde21e037de8231e0c529961dd2a887a GIT binary patch literal 401 zcmV;C0dD?@P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzGf6~2RCwBA{Qv(y12q5>1C;>P3}OIz z4IS(1>Ymrp(J=%{Ga_NKTx7t=$aoHD*aRgdB@w7PMzXyIG%SysoBNEWre+9`%Yx59 zV!}v{g@t7W&~@unfdI&72I)hOZK4Ak=sF(`4vv%B+S)}R*8%y=$gU$b*7%v2m^y)m z^#j>#P}hMCV<FL?Pd|VDY`S&p))J^e|DpIF(FXnb^Jm@Dr%ziyefsns>Hucsz-J*M zojv*d`E%vNhYwGHYzCPL#ebpv{{#&Jx}x{`_3KliHiI<(2I5~x_z%o5Tm~Kd`t@tp zy?giGLc@Xqs_`e10VpnF#FqPkQOg1~fCCy9|Bzhq7m86d;&Rb{sN$c{!2FBkivQ?_ v;4|nSlK)}+f7lEl#zp^-3?f+i0R$KT7f^r&QbV%300000NkvXXu0mjf&6u(c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/particleEditor/main.cs b/Templates/BaseGame/game/tools/particleEditor/main.cs new file mode 100644 index 000000000..0283b35c3 --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/main.cs @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Initialization and shutdown code for particle editor plugin. + + +//--------------------------------------------------------------------------------------------- + +function initializeParticleEditor() +{ + echo( " % - Initializing Particle Editor" ); + + exec( "./ParticleEditor.ed.gui" ); + exec( "./particleEditor.ed.cs" ); + exec( "./particleEditorUndo.ed.cs" ); + exec( "./particleEmitterEditor.ed.cs" ); + exec( "./particleParticleEditor.ed.cs" ); + + PE_Window.setVisible( false ); + EditorGui.add( PE_Window ); + + new ScriptObject( ParticleEditorPlugin ) + { + superClass = "WorldEditorPlugin"; + editorGui = EWorldEditor; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "1", "EWorldEditorNoneModeBtn.performClick();", "" ); // Select + %map.bindCmd( keyboard, "2", "EWorldEditorMoveModeBtn.performClick();", "" ); // Move + %map.bindCmd( keyboard, "3", "EWorldEditorRotateModeBtn.performClick();", "" ); // Rotate + %map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale + + ParticleEditorPlugin.map = %map; + + new ScriptObject( ParticleEditor ); + + new PersistenceManager( PE_EmitterSaver ); + new PersistenceManager( PE_ParticleSaver ); + + new SimSet( PE_UnlistedParticles ); + new SimSet( PE_UnlistedEmitters ); +} + +//--------------------------------------------------------------------------------------------- + +function destroyParticleEditor() +{ +} + +//============================================================================================= +// ParticleEditorPlugin. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Particle Editor", "", ParticleEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Particle Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "ParticleEditorPlugin", "ParticleEditorPalette", expandFilename("tools/worldEditor/images/toolbar/particleeditor"), %tooltip ); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::onActivated( %this ) +{ + if( !ParticleEditor.isInitialized ) + { + ParticleEditor.initEditor(); + ParticleEditor.isInitialized = true; + } + + EditorGui-->WorldEditorToolbar.setVisible( true ); + EditorGui.bringToFront( PE_Window); + PE_Window.setVisible( true ); + PE_Window.makeFirstResponder( true ); + + %this.map.push(); + + ParticleEditor.resetEmitterNode(); + + // Set the status bar here + EditorGuiStatusBar.setInfo( "Particle editor." ); + EditorGuiStatusBar.setSelection( "" ); + + Parent::onActivated( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::onDeactivated( %this ) +{ + EditorGui-->WorldEditorToolbar.setVisible( false ); + PE_Window.setVisible( false ); + + if( isObject( $ParticleEditor::emitterNode) ) + $ParticleEditor::emitterNode.delete(); + + %this.map.pop(); + + Parent::onDeactivated( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::onExitMission( %this ) +{ + // Force Particle Editor to re-initialize. + ParticleEditor.isInitialized = false; + + Parent::onExitMission( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "ParticleEditor", true ); + + EditorSettings.setDefaultValue( "selectedTab", 0 ); + + EditorSettings.endGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "ParticleEditor", true ); + + %selectedEmitter = EditorSettings.value( "selectedEmitter" ); + if( isObject( %selectedEmitter ) ) + PEE_EmitterSelector.setSelected( %selectedEmitter.getId() ); + + %selectedParticle = EditorSettings.value( "selectedParticle" ); + if( isObject( %selectedParticle ) ) + PEP_ParticleSelector.setSelected( %selectedParticle.getId() ); + + PE_TabBook.selectPage( EditorSettings.value( "selectedPage" ) ); + + EditorSettings.endGroup(); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "ParticleEditor", true ); + + EditorSettings.setValue( "selectedEmitter", PEE_EmitterSelector.getText() ); + EditorSettings.setValue( "selectedParticle", PEP_ParticleSelector.getText() ); + EditorSettings.setValue( "selectedTab", PE_TabBook.getSelectedPage() ); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.cs b/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.cs new file mode 100644 index 000000000..4156e38d6 --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/particleEditor.ed.cs @@ -0,0 +1,255 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +// Open the particle editor to spawn a test emitter in front of the player. +// Edit the sliders, check boxes, and text fields and see the results in +// realtime. Switch between emitters and particles with the buttons in the +// top left corner. When in particle mode, the only particles available will +// be those assigned to the current emitter to avoid confusion. In the top +// right corner, there is a button marked "Drop Emitter", which will spawn the +// test emitter in front of the player again, and a button marked "Restart +// Emitter", which will play the particle animation again. + + +//============================================================================================= +// ParticleEditor. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::initEditor( %this ) +{ + echo( "Initializing ParticleEmitterData and ParticleData DataBlocks..." ); + + datablock ParticleEmitterData(PE_EmitterEditor_NotDirtyEmitter) + { + particles = "DefaultParticle"; + }; + datablock ParticleData(PE_ParticleEditor_NotDirtyParticle) + { + textureName = "art/particles/defaultParticle"; + }; + + PE_UnlistedEmitters.add( PE_EmitterEditor_NotDirtyEmitter ); + PE_UnlistedEmitters.add( PE_ParticleEditor_NotDirtyParticle ); + + PEE_EmitterSelector.clear(); + PEE_EmitterParticleSelector1.clear(); + PEE_EmitterParticleSelector2.clear(); + PEE_EmitterParticleSelector3.clear(); + PEE_EmitterParticleSelector4.clear(); + + PEP_ParticleSelector.clear(); + + ParticleEditor.createParticleList(); + + PEE_EmitterParticleSelector2.add( "None", 0 ); + PEE_EmitterParticleSelector3.add( "None", 0 ); + PEE_EmitterParticleSelector4.add( "None", 0 ); + + PEE_EmitterParticleSelector1.sort(); + PEE_EmitterParticleSelector2.sort(); + PEE_EmitterParticleSelector3.sort(); + PEE_EmitterParticleSelector4.sort(); + + PE_EmitterEditor-->PEE_blendType.clear(); + PE_EmitterEditor-->PEE_blendType.add( "NORMAL", 0 ); + PE_EmitterEditor-->PEE_blendType.add( "ADDITIVE", 1 ); + PE_EmitterEditor-->PEE_blendType.add( "SUBTRACTIVE", 2 ); + PE_EmitterEditor-->PEE_blendType.add( "PREMULTALPHA", 3 ); + + + PEE_EmitterSelector.setFirstSelected(); + + PE_Window-->EditorTabBook.selectPage( 0 ); +} + +function ParticleEditor::createParticleList( %this ) +{ + // This function creates the list of all particles and particle emitters + + %emitterCount = 0; + %particleCount = 0; + + foreach( %obj in DatablockGroup ) + { + if( %obj.isMemberOfClass( "ParticleEmitterData" ) ) + { + // Filter out emitters on the PE_UnlistedEmitters list. + + %unlistedFound = false; + foreach( %unlisted in PE_UnlistedEmitters ) + if( %unlisted.getId() == %obj.getId() ) + { + %unlistedFound = true; + break; + } + + if( %unlistedFound ) + continue; + + // To prevent our default emitters from getting changed, + // prevent them from populating the list. Default emitters + // should only be used as a template for creating new ones. + if ( %obj.getName() $= "DefaultEmitter") + continue; + + PEE_EmitterSelector.add( %obj.getName(), %obj.getId() ); + %emitterCount ++; + } + else if( %obj.isMemberOfClass( "ParticleData" ) ) + { + %unlistedFound = false; + foreach( %unlisted in PE_UnlistedParticles ) + if( %unlisted.getId() == %obj.getId() ) + { + %unlistedFound = true; + break; + } + + if( %unlistedFound ) + continue; + + %name = %obj.getName(); + %id = %obj.getId(); + + if ( %name $= "DefaultParticle") + continue; + + // Add to particle dropdown selectors. + + PEE_EmitterParticleSelector1.add( %name, %id ); + PEE_EmitterParticleSelector2.add( %name, %id ); + PEE_EmitterParticleSelector3.add( %name, %id ); + PEE_EmitterParticleSelector4.add( %name, %id ); + + %particleCount ++; + } + } + + PEE_EmitterSelector.sort(); + PEE_EmitterParticleSelector1.sort(); + PEE_EmitterParticleSelector2.sort(); + PEE_EmitterParticleSelector3.sort(); + PEE_EmitterParticleSelector4.sort(); + + echo( "Found" SPC %emitterCount SPC "emitters and" SPC %particleCount SPC "particles." ); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::openEmitterPane( %this ) +{ + PE_Window.text = "Particle Editor - Emitters"; + PE_EmitterEditor.guiSync(); + ParticleEditor.activeEditor = PE_EmitterEditor; + + if( !PE_EmitterEditor.dirty ) + PE_EmitterEditor.setEmitterNotDirty(); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::openParticlePane( %this ) +{ + PE_Window.text = "Particle Editor - Particles"; + + PE_ParticleEditor.guiSync(); + ParticleEditor.activeEditor = PE_ParticleEditor; + + if( !PE_ParticleEditor.dirty ) + PE_ParticleEditor.setParticleNotDirty(); +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::resetEmitterNode( %this ) +{ + %tform = ServerConnection.getControlObject().getEyeTransform(); + %vec = VectorNormalize( ServerConnection.getControlObject().getForwardVector() ); + %vec = VectorScale( %vec, 4 ); + %tform = setWord( %tform, 0, getWord( %tform, 0 ) + getWord( %vec, 0 ) ); + %tform = setWord( %tform, 1, getWord( %tform, 1 ) + getWord( %vec, 1 ) ); + %tform = setWord( %tform, 2, getWord( %tform, 2 ) + getWord( %vec, 2 ) ); + + if( !isObject( $ParticleEditor::emitterNode ) ) + { + if( !isObject( TestEmitterNodeData ) ) + { + datablock ParticleEmitterNodeData( TestEmitterNodeData ) + { + timeMultiple = 1; + }; + } + + $ParticleEditor::emitterNode = new ParticleEmitterNode() + { + emitter = PEE_EmitterSelector.getText(); + velocity = 1; + position = getWords( %tform, 0, 2 ); + rotation = getWords( %tform, 3, 6 ); + datablock = TestEmitterNodeData; + parentGroup = MissionCleanup; + }; + } + else + { + $ParticleEditor::emitterNode.setTransform( %tform ); + + %clientObject = $ParticleEditor::emitterNode.getClientObject(); + if( isObject( %clientObject ) ) + %clientObject.setTransform( %tform ); + + ParticleEditor.updateEmitterNode(); + } +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::updateEmitterNode( %this ) +{ + if( isObject( $ParticleEditor::emitterNode ) ) + { + %id = PEE_EmitterSelector_Control-->PopUpMenu.getSelected(); + + %clientObject = $ParticleEditor::emitterNode.getClientObject(); + if( isObject( %clientObject ) ) + %clientObject.setEmitterDataBlock( %id ); + } + else + %this.resetEmitterNode(); +} + +//============================================================================================= +// PE_TabBook. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PE_TabBook::onTabSelected( %this, %text, %idx ) +{ + if( %idx == 0 ) + ParticleEditor.openEmitterPane(); + else + ParticleEditor.openParticlePane(); +} diff --git a/Templates/BaseGame/game/tools/particleEditor/particleEditorUndo.ed.cs b/Templates/BaseGame/game/tools/particleEditor/particleEditorUndo.ed.cs new file mode 100644 index 000000000..f1e753d5e --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/particleEditorUndo.ed.cs @@ -0,0 +1,606 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//============================================================================================= +// ParticleEditor. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::createUndo( %this, %class, %desc ) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = %class; + superClass = BaseParticleEdAction; + actionName = %desc; + }; + popInstantGroup(); + return %action; +} + +//--------------------------------------------------------------------------------------------- + +function ParticleEditor::submitUndo( %this, %action ) +{ + %action.addToManager( Editor.getUndoManager() ); +} + +//============================================================================================= +// BaseParticleEdAction. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function BaseParticleEdAction::sync( %this ) +{ + // Sync particle state. + + if( isObject( %this.particle ) ) + { + %this.particle.reload(); + PE_ParticleEditor.guiSync(); + + if( %this.particle.getId() == PE_ParticleEditor.currParticle.getId() ) + PE_ParticleEditor.setParticleDirty(); + } + + // Sync emitter state. + + if( isObject( %this.emitter ) ) + { + %this.emitter.reload(); + + PE_EmitterEditor.guiSync(); + + if( %this.emitter.getId() == PE_EmitterEditor.currEmitter.getId() ) + PE_EmitterEditor.setEmitterDirty(); + } +} + +//--------------------------------------------------------------------------------------------- + +function BaseParticleEdAction::redo( %this ) +{ + %this.sync(); +} + +//--------------------------------------------------------------------------------------------- + +function BaseParticleEdAction::undo( %this ) +{ + %this.sync(); +} + +//============================================================================================= +// ActionRenameEmitter. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +//TODO + +//============================================================================================= +// ActionCreateNewEmitter. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionCreateNewEmitter::redo( %this ) +{ + %emitter = %this.emitter; + + // Assign name. + + %emitter.name = %this.emitterName; + + // Remove from unlisted. + + PE_UnlistedEmitters.remove( %emitter ); + + // Drop it in the dropdown and select it. + + %popup = PEE_EmitterSelector; + + %popup.add( %emitter.getName(), %emitter.getId() ); + %popup.sort(); + %popup.setSelected( %emitter.getId() ); + + // Sync up. + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionCreateNewEmitter::undo( %this ) +{ + %emitter = %this.emitter; + + // Prevent a save dialog coming up on the emitter. + + if( %emitter == PE_EmitterEditor.currEmitter ) + PE_EmitterEditor.setEmitterNotDirty(); + + // Add to unlisted. + + PE_UnlistedEmitters.add( %emitter ); + + // Remove it from in the dropdown and select prev emitter. + + %popup = PEE_EmitterSelector; + + if( isObject( %this.prevEmitter ) ) + %popup.setSelected( %this.prevEmitter.getId() ); + else + %popup.setFirstSelected(); + + %popup.clearEntry( %emitter.getId() ); + + // Unassign name. + + %this.emitterName = %emitter.name; + %emitter.name = ""; + + // Sync up. + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionDeleteEmitter. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteEmitter::redo( %this ) +{ + %emitter = %this.emitter; + + // Unassign name. + + %this.emitterName = %emitter.name; + %emitter.name = ""; + + // Add to unlisted. + + PE_UnlistedEmitters.add( %emitter ); + + // Remove from file. + + if( %emitter.getFileName() !$= "" + && %emitter.getFilename() !$= "tools/particleEditor/particleEmitterEditor.ed.cs" ) + PE_EmitterSaver.removeObjectFromFile( %emitter ); + + // Select DefaultEmitter or first in list. + + %popup = PEE_EmitterSelector_Control-->PopUpMenu; + + %popup.setFirstSelected(); + + // Remove from dropdown. + + %popup.clearEntry( %emitter ); + + // Sync up. + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteEmitter::undo( %this ) +{ + %emitter = %this.emitter; + + // Re-assign name. + + %emitter.name = %this.emitterName; + + // Remove from unlisted. + + PE_UnlistedEmitters.remove( %emitter ); + + // Resave to file. + + if( %this.emitterFname !$= "" + && %this.emitterFname !$= "tools/particleEditor/particleEmitterEditor.ed.gui" ) + { + PE_EmitterSaver.setDirty( %emitter, %this.emitterFname ); + PE_EmitterSaver.saveDirty(); + } + + // Add it to the dropdown and selet it. + + %popup = PEE_EmitterSelector_Control-->PopUpMenu; + %popup.add( %emitter.getName(), %emitter.getId() ); + %popup.sort(); + %popup.setSelected( %emitter.getId() ); + + // Sync up. + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveEmitter. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitter::redo( %this ) +{ + %emitter = %this.emitter; + %emitter.setFieldValue( %this.field, %this.newValue ); + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitter::undo( %this ) +{ + %emitter = %this.emitter; + %emitter.setFieldValue( %this.field, %this.oldValue ); + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveEmitterLifeFields. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterLifeFields::redo( %this ) +{ + %emitter = %this.emitter; + + %emitter.lifetimeMS = %this.newValueLifetimeMS; + %emitter.lifetimeVarianceMS = %this.newValueLifetimeVarianceMS; + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterLifeFields::undo( %this ) +{ + %emitter = %this.emitter; + + %emitter.lifetimeMS = %this.oldValueLifetimeMS; + %emitter.lifetimeVarianceMS = %this.oldValueLifetimeVarianceMS; + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveEmitterAmountFields. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterAmountFields::redo( %this ) +{ + %emitter = %this.emitter; + + %emitter.ejectionPeriodMS = %this.newValueEjectionPeriodMS; + %emitter.periodVarianceMS = %this.newValuePeriodVarianceMS; + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterAmountFields::undo( %this ) +{ + %emitter = %this.emitter; + + %emitter.ejectionPeriodMS = %this.oldValueEjectionPeriodMS; + %emitter.periodVarianceMS = %this.oldValuePeriodVarianceMS; + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveEmitterSpeedFields. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterSpeedFields::redo( %this ) +{ + %emitter = %this.emitter; + + %emitter.ejectionVelocity = %this.newValueEjectionVelocity; + %emitter.velocityVariance = %this.newValueVelocityVariance; + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveEmitterSpeedFields::undo( %this ) +{ + %emitter = %this.emitter; + + %emitter.ejectionVelocity = %this.oldValueEjectionVelocity; + %emitter.velocityVariance = %this.oldValueVelocityVariance; + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionCreateNewParticle. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionCreateNewParticle::redo( %this ) +{ + %particle = %this.particle.getName(); + %particleId = %this.particle.getId(); + %particleIndex = %this.particleIndex; + %emitter = %this.emitter; + + // Remove from unlisted. + + PE_UnlistedParticles.remove( %particleId ); + + // Add it to the dropdown. + + PEP_ParticleSelector.add( %particle, %particleId ); + PEP_ParticleSelector.sort(); + PEP_ParticleSelector.setSelected( %particleId, false ); + + // Add particle to dropdowns in the emitter editor. + + for( %i = 1; %i < 5; %i ++ ) + { + %emitterParticle = "PEE_EmitterParticle" @ %i; + %popup = %emitterParticle-->PopupMenu; + + %popup.add( %particle, %particleId ); + %popup.sort(); + + if( %i == %particleIndex + 1 ) + %popup.setSelected( %particleId ); + } + + // Sync up. + + PE_ParticleEditor.loadNewParticle(); + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionCreateNewParticle::undo( %this ) +{ + %particle = %this.particle.getName(); + %particleId = %this.particle.getId(); + %emitter = %this.emitter; + + // Add to unlisted. + + PE_UnlistedParticles.add( %particleId ); + + // Remove from dropdown. + + PEP_ParticleSelector.clearEntry( %particleId ); + PEP_ParticleSelector.setFirstSelected( false ); + + // Remove from particle dropdowns in emitter editor. + + for( %i = 1; %i < 5; %i ++ ) + { + %emitterParticle = "PEE_EmitterParticle" @ %i; + %popup = %emitterParticle-->PopUpMenu; + + if( %popup.getSelected() == %particleId ) + %popup.setSelected( %this.prevParticle ); + + %popup.clearEntry( %particleId ); + } + + // Sync up. + + PE_ParticleEditor.loadNewParticle(); + Parent::undo( %this ); +} + +//============================================================================================= +// ActionDeleteParticle. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteParticle::redo( %this ) +{ + %particle = %this.particle.getName(); + %particleId = %this.particle.getId(); + %emitter = %this.emitter; + + // Add to unlisted. + + PE_UnlistedParticles.add( %particleId ); + + // Remove from file. + + if( %particle.getFileName() !$= "" + && %particle.getFilename() !$= "tools/particleEditor/particleParticleEditor.ed.cs" ) + PE_ParticleSaver.removeObjectFromFile( %particleId ); + + // Remove from dropdown. + + PEP_ParticleSelector.clearEntry( %particleId ); + PEP_ParticleSelector.setFirstSelected(); + + // Remove from particle selectors in emitter. + + for( %i = 1; %i < 5; %i ++ ) + { + %emitterParticle = "PEE_EmitterParticle" @ %i; + %popup = %emitterParticle-->PopUpMenu; + + if( %popup.getSelected() == %particleId ) + { + %this.particleIndex = %i - 1; + %popup.setSelected( 0 ); // Select "None". + } + + %popup.clearEntry( %particleId ); + } + + // Sync up. + + PE_ParticleEditor.loadNewParticle(); + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionDeleteParticle::undo( %this ) +{ + %particle = %this.particle.getName(); + %particleId = %this.particle.getId(); + %particleIndex = %this.particleIndex; + %emitter = %this.emitter; + + // Remove from unlisted. + + PE_UnlistedParticles.remove( %particleId ); + + // Resave to file. + + if( %particle.getFilename() !$= "" + && %particle.getFilename() !$= "tools/particleEditor/particleParticleEditor.ed.gui" ) + { + PE_ParticleSaver.setDirty( %particle ); + PE_ParticleSaver.saveDirty(); + } + + // Add to dropdown. + + PEP_ParticleSelector.add( %particle, %particleId ); + PEP_ParticleSelector.sort(); + PEP_ParticleSelector.setSelected( %particleId ); + + // Add particle to dropdowns in the emitter editor. + + for( %i = 1; %i < 5; %i ++ ) + { + %emitterParticle = "PEE_EmitterParticle" @ %i; + %popup = %emitterParticle-->PopUpMenu; + + %popup.add( %particle, %particleId ); + %popup.sort(); + + if( %i == %particleIndex + 1 ) + %popup.setSelected( %particleId ); + } + + // Sync up. + + PE_ParticleEditor.loadNewParticle(); + Parent::undo( %This ); +} + +//============================================================================================= +// ActionUpdateActiveParticle. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveParticle::redo( %this ) +{ + %particle = %this.particle; + %particle.setFieldValue( %this.field, %this.newValue ); + + Parent::redo( %this ); +} + +function ActionUpdateActiveParticle::undo( %this ) +{ + %particle = %this.particle; + %particle.setFieldValue( %this.field, %this.oldValue ); + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveParticleLifeFields. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveParticleLifeFields::redo( %this ) +{ + %particle = %this.particle; + + %particle.lifetimeMS = %this.newValueLifetimeMS; + %particle.lifetimeVarianceMS = %this.newValueLifetimeVarianceMS; + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveParticleLifeFields::undo( %this ) +{ + %particle = %this.particle; + + %particle.lifetimeMS = %this.oldValueLifetimeMS; + %particle.lifetimeVarianceMS = %this.oldValueLifetimeVarianceMS; + + Parent::undo( %this ); +} + +//============================================================================================= +// ActionUpdateActiveParticleSpinFields. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveParticleSpinFields::redo( %this ) +{ + %particle = %this.particle; + + %particle.spinRandomMax = %this.newValueSpinRandomMax; + %particle.spinRandomMin = %this.newValueSpinRandomMin; + + Parent::redo( %this ); +} + +//--------------------------------------------------------------------------------------------- + +function ActionUpdateActiveParticleSpinFields::undo( %this ) +{ + %particle = %this.particle; + + %particle.spinRandomMax = %this.oldValueSpinRandomMax; + %particle.spinRandomMin = %this.oldValueSpinRandomMin; + + Parent::undo( %this ); +} diff --git a/Templates/BaseGame/game/tools/particleEditor/particleEmitterEditor.ed.cs b/Templates/BaseGame/game/tools/particleEditor/particleEmitterEditor.ed.cs new file mode 100644 index 000000000..4e3a571e7 --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/particleEmitterEditor.ed.cs @@ -0,0 +1,659 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +$PE_EMITTEREDITOR_DEFAULT_FILENAME = "art/particles/managedParticleEmitterData.cs"; + + +//============================================================================================= +// PE_EmitterEditor. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::guiSync( %this ) +{ + %data = PE_EmitterEditor.currEmitter; + + // Sync up sliders and number boxes. + + if( PE_EmitterEditor-->PEE_infiniteLoop.isStateOn() ) + { + PE_EmitterEditor-->PEE_lifetimeMS_slider.setActive( false ); + PE_EmitterEditor-->PEE_lifetimeMS_textEdit.setActive( false ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_slider.setActive( false ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.setActive( false ); + } + else + { + PE_EmitterEditor-->PEE_lifetimeMS_slider.setActive( true ); + PE_EmitterEditor-->PEE_lifetimeMS_textEdit.setActive( true ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_slider.setActive( true ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.setActive( true ); + + PE_EmitterEditor-->PEE_lifetimeMS_slider.setValue( %data.lifetimeMS ); + PE_EmitterEditor-->PEE_lifetimeMS_textEdit.setText( %data.lifetimeMS ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_slider.setValue( %data.lifetimeVarianceMS ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.setText( %data.lifetimeVarianceMS ); + } + + PE_EmitterEditor-->PEE_ejectionPeriodMS_slider.setValue( %data.ejectionPeriodMS ); + PE_EmitterEditor-->PEE_ejectionPeriodMS_textEdit.setText( %data.ejectionPeriodMS ); + + PE_EmitterEditor-->PEE_periodVarianceMS_slider.setValue( %data.periodVarianceMS ); + PE_EmitterEditor-->PEE_periodVarianceMS_textEdit.setText( %data.periodVarianceMS ); + + PE_EmitterEditor-->PEE_ejectionVelocity_slider.setValue( %data.ejectionVelocity ); + PE_EmitterEditor-->PEE_ejectionVelocity_textEdit.setText( %data.ejectionVelocity ); + + PE_EmitterEditor-->PEE_velocityVariance_slider.setValue( %data.velocityVariance ); + PE_EmitterEditor-->PEE_velocityVariance_textEdit.setText( %data.velocityVariance ); + + PE_EmitterEditor-->PEE_orientParticles.setValue( %data.orientParticles ); + PE_EmitterEditor-->PEE_alignParticles.setValue( %data.alignParticles ); + PE_EmitterEditor-->PEE_alignDirection.setText( %data.alignDirection ); + + PE_EmitterEditor-->PEE_thetaMin_slider.setValue( %data.thetaMin ); + PE_EmitterEditor-->PEE_thetaMin_textEdit.setText( %data.thetaMin ); + + PE_EmitterEditor-->PEE_thetaMax_slider.setValue( %data.thetaMax ); + PE_EmitterEditor-->PEE_thetaMax_textEdit.setText( %data.thetaMax ); + + PE_EmitterEditor-->PEE_phiVariance_slider.setValue( %data.phiVariance ); + PE_EmitterEditor-->PEE_phiVariance_textEdit.setText( %data.phiVariance ); + + PE_EmitterEditor-->PEE_ejectionOffset_slider.setValue( %data.ejectionOffset ); + PE_EmitterEditor-->PEE_ejectionOffset_textEdit.setText( %data.ejectionOffset ); + + PE_EmitterEditor-->PEE_ejectionOffsetVariance_slider.setValue( %data.ejectionOffsetVariance ); + PE_EmitterEditor-->PEE_ejectionOffsetVariance_textEdit.setText( %data.ejectionOffsetVariance ); + + %blendTypeId = PE_EmitterEditor-->PEE_blendType.findText( %data.blendStyle ); + PE_EmitterEditor-->PEE_blendType.setSelected( %blendTypeId, false ); + + PE_EmitterEditor-->PEE_softnessDistance_slider.setValue( %data.softnessDistance ); + PE_EmitterEditor-->PEE_softnessDistance_textEdit.setText( %data.softnessDistance ); + + PE_EmitterEditor-->PEE_ambientFactor_slider.setValue( %data.ambientFactor ); + PE_EmitterEditor-->PEE_ambientFactor_textEdit.setText( %data.ambientFactor ); + + PE_EmitterEditor-->PEE_softParticles.setValue( %data.softParticles ); + PE_EmitterEditor-->PEE_reverseOrder.setValue( %data.reverseOrder ); + PE_EmitterEditor-->PEE_useEmitterSizes.setValue( %data.useEmitterSizes ); + PE_EmitterEditor-->PEE_useEmitterColors.setValue( %data.useEmitterColors ); + + PE_EmitterEditor-->PEE_glow.setValue( %data.glow ); + + // Sync up particle selectors. + + for( %index = 0; %index < 4; %index ++ ) + { + %ctrl = "PEE_EmitterParticle" @ ( %index + 1 ); + %popup = %ctrl-->PopUpMenu; + + %particle = getWord( %data.particles, %index ); + if( isObject( %particle ) ) + %popup.setSelected( %particle.getId(), false ); + else + %popup.setSelected( 0, false ); // Select "None". + } +} + +//--------------------------------------------------------------------------------------------- + +// Generic updateEmitter method +function PE_EmitterEditor::updateEmitter( %this, %propertyField, %value, %isSlider, %onMouseUp ) +{ + PE_EmitterEditor.setEmitterDirty(); + + %emitter = PE_EmitterEditor.currEmitter; + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.field = %propertyField; + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValue = %value; + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveEmitter, "Update Active Emitter"); + %action.emitter = %emitter; + %action.field = %propertyField; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + %action.newValue = %value; + %action.oldValue = %emitter.getFieldValue( %propertyField ); + + ParticleEditor.submitUndo( %action ); + } + + %emitter.setFieldValue( %propertyField, %value ); + %emitter.reload(); +} + +//--------------------------------------------------------------------------------------------- + +// Special case updateEmitter methods +function PE_EmitterEditor::updateLifeFields( %this, %isRandom, %value, %isSlider, %onMouseUp ) +{ + PE_EmitterEditor.setEmitterDirty(); + + %emitter = PE_EmitterEditor.currEmitter; + + // Transfer values over to gui controls. + + if( %isRandom ) + { + if( %value > 0 ) + %value++; + + if( %value > PE_EmitterEditor-->PEE_lifetimeMS_slider.getValue() ) + { + PE_EmitterEditor-->PEE_lifetimeMS_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_lifetimeMS_slider.setValue( %value ); + } + } + else + { + if( %value > 0 ) + %value --; + + if( %value < PE_EmitterEditor-->PEE_lifetimeVarianceMS_slider.getValue() ) + { + PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_lifetimeVarianceMS_slider.setValue( %value ); + } + } + + // Submit undo. + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValueLifetimeMS = PE_EmitterEditor-->PEE_lifetimeMS_textEdit.getText(); + %last.newValueLifetimeVarianceMS = PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.getText(); + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveEmitterLifeFields, "Update Active Emitter"); + %action.emitter = %emitter; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + + %action.newValueLifetimeMS = PE_EmitterEditor-->PEE_lifetimeMS_textEdit.getText(); + %action.oldValueLifetimeMS = %emitter.lifetimeMS; + + %action.newValueLifetimeVarianceMS = PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.getText(); + %action.oldValueLifetimeVarianceMS = %emitter.lifetimeVarianceMS; + + ParticleEditor.submitUndo( %action ); + } + + // Set the values on the current emitter. + + %emitter.lifetimeMS = PE_EmitterEditor-->PEE_lifetimeMS_textEdit.getText(); + %emitter.lifetimeVarianceMS = PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.getText(); + %emitter.reload(); + + // Keep the infiniteLoop checkbox up to date. + + PE_EmitterEditor-->PEE_infiniteLoop.setStateOn( + %emitter.lifetimeMS == 0 + ); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::updateLifeFieldsInfiniteLoop( %this ) +{ + %emitter = PE_EmitterEditor.currEmitter; + %isEnabled = PE_EmitterEditor-->PEE_infiniteLoop.isStateOn(); + + // Submit undo. + + %action = ParticleEditor.createUndo( ActionUpdateActiveEmitterLifeFields, "Update Active Emitter" ); + %action.emitter = %emitter; + + if( %isEnabled ) + { + %action.newValueLifetimeMS = 0; + %action.newvalueLifetimeVarianceMS = 0; + %action.oldValueLifetimeMS = PE_EmitterEditor-->PEE_lifetimeMS_textEdit.getText(); + %action.oldValueLifetimeVarianceMS = PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.getText(); + } + else + { + %action.newValueLifetimeMS = PE_EmitterEditor-->PEE_lifetimeMS_textEdit.getText(); + %action.newvalueLifetimeVarianceMS = PE_EmitterEditor-->PEE_lifetimeVarianceMS_textEdit.getText(); + %action.oldValueLifetimeMS = 0; + %action.oldValueLifetimeVarianceMS = 0; + } + + ParticleEditor.submitUndo( %action ); + + // Execute action. + + %action.redo(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::updateAmountFields( %this, %isRandom, %value, %isSlider, %onMouseUp ) +{ + PE_EmitterEditor.setEmitterDirty(); + %emitter = PE_EmitterEditor.currEmitter; + + // Transfer values over to gui controls. + + if( %isRandom ) + { + %value ++; + if( %value > PE_EmitterEditor-->PEE_ejectionPeriodMS_slider.getValue() ) + { + PE_EmitterEditor-->PEE_ejectionPeriodMS_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_ejectionPeriodMS_slider.setValue( %value ); + } + } + else + { + %value --; + if( %value < PE_EmitterEditor-->PEE_periodVarianceMS_slider.getValue() ) + { + PE_EmitterEditor-->PEE_periodVarianceMS_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_periodVarianceMS_slider.setValue( %value ); + } + } + + // Submit undo. + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValueEjectionPeriodMS = PE_EmitterEditor-->PEE_ejectionPeriodMS_textEdit.getText(); + %last.newValuePeriodVarianceMS = PE_EmitterEditor-->PEE_periodVarianceMS_textEdit.getText(); + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveEmitterAmountFields, "Update Active Emitter"); + %action.emitter = %emitter; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + + %action.newValueEjectionPeriodMS = PE_EmitterEditor-->PEE_ejectionPeriodMS_textEdit.getText(); + %action.oldValueEjectionPeriodMS = %emitter.ejectionPeriodMS; + + %action.newValuePeriodVarianceMS = PE_EmitterEditor-->PEE_periodVarianceMS_textEdit.getText(); + %action.oldValuePeriodVarianceMS = %emitter.periodVarianceMS; + + ParticleEditor.submitUndo( %action ); + } + + // Set the values on the current emitter. + + %emitter.ejectionPeriodMS = PE_EmitterEditor-->PEE_ejectionPeriodMS_textEdit.getText(); + %emitter.periodVarianceMS = PE_EmitterEditor-->PEE_periodVarianceMS_textEdit.getText(); + %emitter.reload(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::updateSpeedFields( %this, %isRandom, %value, %isSlider, %onMouseUp ) +{ + PE_EmitterEditor.setEmitterDirty(); + %emitter = PE_EmitterEditor.currEmitter; + + // Transfer values over to gui controls. + + if( %isRandom ) + { + if( %value > PE_EmitterEditor-->PEE_ejectionVelocity_slider.getValue() ) + { + PE_EmitterEditor-->PEE_ejectionVelocity_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_ejectionVelocity_slider.setValue( %value ); + } + } + else + { + if( %value < PE_EmitterEditor-->PEE_velocityVariance_slider.getValue() ) + { + PE_EmitterEditor-->PEE_velocityVariance_textEdit.setText( %value ); + PE_EmitterEditor-->PEE_velocityVariance_slider.setValue( %value ); + } + } + + // Submit undo. + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValueEjectionVelocity = PE_EmitterEditor-->PEE_ejectionVelocity_textEdit.getText(); + %last.newValueVelocityVariance = PE_EmitterEditor-->PEE_velocityVariance_textEdit.getText(); + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveEmitterSpeedFields, "Update Active Emitter"); + %action.emitter = %emitter; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + + %action.newValueEjectionVelocity = PE_EmitterEditor-->PEE_ejectionVelocity_textEdit.getText(); + %action.oldValueEjectionVelocity = %emitter.ejectionVelocity; + + %action.newValueVelocityVariance = PE_EmitterEditor-->PEE_velocityVariance_textEdit.getText(); + %action.oldValueVelocityVariance = %emitter.velocityVariance; + + ParticleEditor.submitUndo( %action ); + } + + // Set the values on the current emitter. + + %emitter.ejectionVelocity = PE_EmitterEditor-->PEE_ejectionVelocity_textEdit.getText(); + %emitter.velocityVariance = PE_EmitterEditor-->PEE_velocityVariance_textEdit.getText(); + %emitter.reload(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::updateParticlesFields( %this ) +{ + %particles = ""; + for( %i = 1; %i < 5; %i ++ ) + { + %emitterParticle = "PEE_EmitterParticle" @ %i; + %popup = %emitterParticle-->PopUpMenu; + %text = %popup.getText(); + + if( %text $= "" || %text $= "None" ) + continue; + + if( %particles $= "" ) + %particles = %text; + else + %particles = %particles SPC %text; + } + + %changedEditParticle = 1; + %currParticle = PE_ParticleEditor.currParticle.getName(); + + foreach$( %particleName in %particles ) + { + if( %particleName $= %currParticle ) + { + %changedEditParticle = 0; + break; + } + } + + // True only if the currently edited particle has not been found and the + // ParticleEditor is dirty. + + if( %changedEditParticle && PE_ParticleEditor.dirty ) + { + MessageBoxYesNoCancel("Save Particle Changes?", + "Do you wish to save the changes made to the <br>current particle before changing the particle?", + "PE_ParticleEditor.saveParticle( " @ PE_ParticleEditor.currParticle.getName() @ " ); PE_EmitterEditor.updateEmitter( \"particles\"," @ %particles @ ");", + "PE_ParticleEditor.saveParticleDialogDontSave( " @ PE_ParticleEditor.currParticle.getName() @ " ); PE_EmitterEditor.updateEmitter( \"particles\"," @ %particles @ ");", + "PE_EmitterEditor.guiSync();" ); + } + else + { + PE_EmitterEditor.updateEmitter( "particles", %particles ); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::onNewEmitter( %this ) +{ + if( isObject( PE_EmitterEditor.currEmitter ) + && PE_EmitterEditor.currEmitter $= PEE_EmitterSelector.getSelected() ) + return; + + //FIXME: disregards particle tab dirty state + + if( PE_EmitterEditor.dirty ) + { + + if( PE_ParticleEditor.dirty ) + { + MessageBoxYesNo("Save Existing Particle?", + "Do you want to save changes to <br><br>" @ PE_ParticleEditor.currParticle.getName(), + "PE_ParticleEditor.saveParticle(" @ PE_ParticleEditor.currParticle @ ");" + ); + } + + %savedEmitter = PE_EmitterEditor.currEmitter; + MessageBoxYesNoCancel("Save Existing Emitter?", + "Do you want to save changes to <br><br>" @ %savedEmitter.getName(), + "PE_EmitterEditor.saveEmitter(" @ %savedEmitter@ "); PE_EmitterEditor.loadNewEmitter();", + "PE_EmitterEditor.saveEmitterDialogDontSave(" @ %savedEmitter @ "); PE_EmitterEditor.loadNewEmitter();" + ); + + + } + else + { + PE_EmitterEditor.loadNewEmitter(); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::loadNewEmitter( %this, %emitter ) +{ + if( isObject( %emitter ) ) + %current = %emitter.getId(); + else + %current = PEE_EmitterSelector.getSelected(); + + PE_EmitterEditor.currEmitter = %current; + PE_EmitterEditor_NotDirtyEmitter.assignFieldsFrom( %current ); + PE_EmitterEditor_NotDirtyEmitter.originalName = %current.name; + + PE_EmitterEditor.guiSync(); + PE_EmitterEditor.setEmitterNotDirty(); + + PE_ParticleEditor.loadNewParticle( getWord( %current.particles, 0 ) ); + + ParticleEditor.updateEmitterNode(); + + PE_EmitterEditor-->PEE_infiniteLoop.setStateOn( %current.lifetimeMS == 0 ); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::setEmitterDirty( %this ) +{ + PE_EmitterEditor.text = "Emitter *"; + PE_EmitterEditor.dirty = true; + + %emitter = PE_EmitterEditor.currEmitter; + + if( %emitter.getFilename() $= "" || %emitter.getFilename() $= "tools/particleEditor/particleEmitterEditor.ed.cs" ) + PE_EmitterSaver.setDirty( %emitter, $PE_EMITTEREDITOR_DEFAULT_FILENAME ); + else + PE_EmitterSaver.setDirty( %emitter ); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::setEmitterNotDirty( %this ) +{ + PE_EmitterEditor.text = "Emitter"; + PE_EmitterEditor.dirty = false; + + PE_EmitterSaver.clearAll(); +} + +//--------------------------------------------------------------------------------------------- + +// Create Functionality +function PE_EmitterEditor::showNewDialog( %this ) +{ + //FIXME: disregards particle tab dirty state + + // Open a dialog if the current emitter is dirty. + + if( PE_ParticleEditor.dirty ) + { + MessageBoxYesNo("Save Existing Particle?", + "Do you want to save changes to <br><br>" @ PE_ParticleEditor.currParticle.getName(), + "PE_ParticleEditor.saveParticle(" @ PE_ParticleEditor.currParticle @ ");" + ); + } + + if( PE_EmitterEditor.dirty ) + { + MessageBoxYesNoCancel("Save Emitter Changes?", + "Do you wish to save the changes made to the <br>current emitter before changing the emitter?", + "PE_EmitterEditor.saveEmitter( " @ PE_EmitterEditor.currEmitter.getName() @ " ); PE_EmitterEditor.createEmitter();", + "PE_EmitterEditor.saveEmitterDialogDontSave( " @ PE_EmitterEditor.currEmitter.getName() @ " ); PE_EmitterEditor.createEmitter();" + ); + } + else + { + PE_EmitterEditor.createEmitter(); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::createEmitter( %this ) +{ + // Create a new emitter. + %emitter = getUniqueName( "newEmitter" ); + datablock ParticleEmitterData( %emitter : DefaultEmitter ) + { + }; + + // Submit undo. + + %action = ParticleEditor.createUndo( ActionCreateNewEmitter, "Create New Emitter" ); + %action.prevEmitter = PE_EmitterEditor.currEmitter; + %action.emitter = %emitter.getId(); + %action.emitterName = %emitter; + + ParticleEditor.submitUndo( %action ); + + // Execute action. + + %action.redo(); + + PE_ParticleEditor.createParticle(false); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::showDeleteDialog( %this ) +{ + if( PE_EmitterEditor.currEmitter.getName() $= "DefaultEmitter" ) + { + MessageBoxOK( "Error", "Cannot delete DefaultEmitter"); + return; + } + + if( isObject( PE_EmitterEditor.currEmitter ) ) + { + MessageBoxYesNoCancel("Delete Emitter?", + "Are you sure you want to delete<br><br>" @ PE_EmitterEditor.currEmitter.getName() @ "<br><br> Emitter deletion won't take affect until the level is exited.", + "PE_EmitterEditor.saveEmitterDialogDontSave( " @ PE_EmitterEditor.currEmitter.getName() @ " ); PE_EmitterEditor.deleteEmitter();" + ); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::deleteEmitter( %this ) +{ + %emitter = PE_EmitterEditor.currEmitter; + + // Create undo. + + %action = ParticleEditor.createUndo( ActionDeleteEmitter, "Delete Emitter" ); + %action.emitter = %emitter; + %action.emitterFname = %emitter.getFilename(); + + ParticleEditor.submitUndo( %action ); + + // Execute action. + + %action.redo(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::saveEmitter( %this, %emitter ) +{ + + + if ( %emitter $= "" ) + %newName = PEE_EmitterSelector_Control->TextEdit.getText(); + else + %newName = %emitter.getName(); + + PE_EmitterEditor.currEmitter.setName( %newName ); + PE_EmitterEditor_NotDirtyEmitter.assignFieldsFrom( %emitter ); + PE_EmitterEditor_NotDirtyEmitter.originalName = %newName; + + PE_EmitterSaver.saveDirty(); + + PE_EmitterEditor.currEmitter = %newName.getId(); + PE_EmitterEditor.setEmitterNotDirty(); + + ParticleEditor.createParticleList(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_EmitterEditor::saveEmitterDialogDontSave( %this, %emitter) +{ + %emitter.setName( PE_EmitterEditor_NotDirtyEmitter.originalName ); + %emitter.assignFieldsFrom( PE_EmitterEditor_NotDirtyEmitter ); + PE_EmitterEditor.setEmitterNotDirty(); +} + +//============================================================================================= +// PEE_EmitterSelector_Control. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PEE_EmitterSelector_Control::onRenameItem( %this ) +{ + Parent::onRenameItem( %this ); + + //FIXME: need to check for validity of name and name clashes + + PE_EmitterEditor.setEmitterDirty(); + + // Resort menu. + + %this-->PopupMenu.sort(); +} diff --git a/Templates/BaseGame/game/tools/particleEditor/particleParticleEditor.ed.cs b/Templates/BaseGame/game/tools/particleEditor/particleParticleEditor.ed.cs new file mode 100644 index 000000000..00a27e5d4 --- /dev/null +++ b/Templates/BaseGame/game/tools/particleEditor/particleParticleEditor.ed.cs @@ -0,0 +1,603 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +$PE_PARTICLEEDITOR_DEFAULT_FILENAME = "art/particles/managedParticleData.cs"; + + +//============================================================================================= +// PE_ParticleEditor. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::guiSync( %this ) +{ + // Populate the selector with the particles assigned + // to the current emitter. + + %containsCurrParticle = false; + %popup = PEP_ParticleSelector; + %popup.clear(); + + foreach$( %particle in PE_EmitterEditor.currEmitter.particles ) + { + if( %particle.getId() == PE_ParticleEditor.currParticle ) + %containsCurrParticle = true; + + %popup.add( %particle, %particle.getId() ); + } + + // Just in case the particle doesn't exist, fallback gracefully + + if( !%containsCurrParticle ) + PE_ParticleEditor.currParticle = getWord( PE_EmitterEditor.currEmitter.particles, 0 ).getId(); + + %data = PE_ParticleEditor.currParticle; + + %popup.sort(); + %popup.setSelected( %data ); + + %bitmap = MaterialEditorGui.searchForTexture( %data.getName(), %data.textureName ); + if( %bitmap !$= "" ) + { + PE_ParticleEditor-->PEP_previewImage.setBitmap( %bitmap ); + PE_ParticleEditor-->PEP_previewImageName.setText( %bitmap ); + PE_ParticleEditor-->PEP_previewImageName.tooltip = %bitmap; + } + else + { + PE_ParticleEditor-->PEP_previewImage.setBitmap( "" ); + PE_ParticleEditor-->PEP_previewImageName.setText( "None" ); + PE_ParticleEditor-->PEP_previewImageName.tooltip = "None"; + } + + PE_ParticleEditor-->PEP_inverseAlpha.setValue( %data.useInvAlpha ); + + PE_ParticleEditor-->PEP_lifetimeMS_slider.setValue( %data.lifetimeMS ); + PE_ParticleEditor-->PEP_lifetimeMS_textEdit.setText( %data.lifetimeMS ); + + PE_ParticleEditor-->PEP_lifetimeVarianceMS_slider.setValue( %data.lifetimeVarianceMS ); + PE_ParticleEditor-->PEP_lifetimeVarianceMS_textEdit.setText( %data.lifetimeVarianceMS ); + + PE_ParticleEditor-->PEP_inheritedVelFactor_slider.setValue( %data.inheritedVelFactor ); + PE_ParticleEditor-->PEP_inheritedVelFactor_textEdit.setText( %data.inheritedVelFactor ); + + PE_ParticleEditor-->PEP_constantAcceleration_slider.setValue( %data.constantAcceleration ); + PE_ParticleEditor-->PEP_constantAcceleration_textEdit.setText( %data.constantAcceleration ); + + PE_ParticleEditor-->PEP_gravityCoefficient_slider.setValue( %data.gravityCoefficient ); + PE_ParticleEditor-->PEP_gravityCoefficient_textEdit.setText( %data.gravityCoefficient ); + + PE_ParticleEditor-->PEP_dragCoefficient_slider.setValue( %data.dragCoefficient ); + PE_ParticleEditor-->PEP_dragCoefficient_textEdit.setText( %data.dragCoefficient ); + + PE_ParticleEditor-->PEP_windCoefficient_slider.setValue( %data.windCoefficient ); + PE_ParticleEditor-->PEP_windCoefficient_textEdit.setText( %data.windCoefficient ); + + PE_ParticleEditor-->PEP_spinRandomMin_slider.setValue( %data.spinRandomMin ); + PE_ParticleEditor-->PEP_spinRandomMin_textEdit.setText( %data.spinRandomMin ); + + PE_ParticleEditor-->PEP_spinRandomMax_slider.setValue( %data.spinRandomMax ); + PE_ParticleEditor-->PEP_spinRandomMax_textEdit.setText( %data.spinRandomMax ); + + PE_ParticleEditor-->PEP_spinRandomMax_slider.setValue( %data.spinRandomMax ); + PE_ParticleEditor-->PEP_spinRandomMax_textEdit.setText( %data.spinRandomMax ); + + PE_ParticleEditor-->PEP_spinSpeed_slider.setValue( %data.spinSpeed ); + PE_ParticleEditor-->PEP_spinSpeed_textEdit.setText( %data.spinSpeed ); + + PE_ColorTintSwatch0.color = %data.colors[ 0 ]; + PE_ColorTintSwatch1.color = %data.colors[ 1 ]; + PE_ColorTintSwatch2.color = %data.colors[ 2 ]; + PE_ColorTintSwatch3.color = %data.colors[ 3 ]; + + PE_ParticleEditor-->PEP_pointSize_slider0.setValue( %data.sizes[ 0 ] ); + PE_ParticleEditor-->PEP_pointSize_textEdit0.setText( %data.sizes[ 0 ] ); + + PE_ParticleEditor-->PEP_pointSize_slider1.setValue( %data.sizes[ 1 ] ); + PE_ParticleEditor-->PEP_pointSize_textEdit1.setText( %data.sizes[ 1 ] ); + + PE_ParticleEditor-->PEP_pointSize_slider2.setValue( %data.sizes[ 2 ] ); + PE_ParticleEditor-->PEP_pointSize_textEdit2.setText( %data.sizes[ 2 ] ); + + PE_ParticleEditor-->PEP_pointSize_slider3.setValue( %data.sizes[ 3 ] ); + PE_ParticleEditor-->PEP_pointSize_textEdit3.setText( %data.sizes[ 3 ] ); + + PE_ParticleEditor-->PEP_pointTime_slider0.setValue( %data.times[ 0 ] ); + PE_ParticleEditor-->PEP_pointTime_textEdit0.setText( %data.times[ 0 ] ); + + PE_ParticleEditor-->PEP_pointTime_slider1.setValue( %data.times[ 1 ] ); + PE_ParticleEditor-->PEP_pointTime_textEdit1.setText( %data.times[ 1 ] ); + + PE_ParticleEditor-->PEP_pointTime_slider2.setValue( %data.times[ 2 ] ); + PE_ParticleEditor-->PEP_pointTime_textEdit2.setText( %data.times[ 2 ] ); + + PE_ParticleEditor-->PEP_pointTime_slider3.setValue( %data.times[ 3 ] ); + PE_ParticleEditor-->PEP_pointTime_textEdit3.setText( %data.times[ 3 ] ); + + //particle animation + PE_ParticleEditor-->PEP_animateTexture.setValue( %data.animateTexture ); + + PE_ParticleEditor-->PEP_framesPerSec_slider.setValue( %data.framesPerSec ); + PE_ParticleEditor-->PEP_framesPerSec_textEdit.setText( %data.framesPerSec ); + + PE_ParticleEditor-->PEP_animTexFramesList_textEdit.setText( %data.animTexFrames ); + + PE_ParticleEditor-->PEP_animTileCount_textEdit.setText( %data.animTexTiling ); + +} + +//--------------------------------------------------------------------------------------------- + +// Generic updateParticle method +function PE_ParticleEditor::updateParticle(%this, %propertyField, %value, %isSlider, %onMouseUp) +{ + PE_ParticleEditor.setParticleDirty(); + %particle = PE_ParticleEditor.currParticle; + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.field = %propertyField; + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValue = %value; + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveParticle, "Update Active Particle"); + %action.particle = %particle; + %action.field = %propertyField; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + %action.newValue = %value; + %action.oldValue = %particle.getFieldValue( %propertyField ); + + ParticleEditor.submitUndo( %action ); + } + + %particle.setFieldValue( %propertyField, %value ); + %particle.reload(); +} + +//--------------------------------------------------------------------------------------------- + +// Special case updateEmitter methods +function PE_ParticleEditor::updateParticleTexture( %this, %action ) +{ + if( %action ) + { + %texture = MaterialEditorGui.openFile("texture"); + if( %texture !$= "" ) + { + PE_ParticleEditor-->PEP_previewImage.setBitmap(%texture); + PE_ParticleEditor-->PEP_previewImageName.setText(%texture); + PE_ParticleEditor-->PEP_previewImageName.tooltip = %texture; + + PE_ParticleEditor.updateParticle( "textureName", %texture ); + } + } + else + { + PE_ParticleEditor-->PEP_previewImage.setBitmap(""); + PE_ParticleEditor-->PEP_previewImageName.setText(""); + PE_ParticleEditor-->PEP_previewImageName.tooltip = ""; + + PE_ParticleEditor.updateParticle( "textureName", "" ); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::updateLifeFields( %this, %isRandom, %value, %isSlider, %onMouseUp ) +{ + PE_ParticleEditor.setParticleDirty(); + %particle = PE_ParticleEditor.currParticle; + + //Transfer values over to gui controls. + + if( %isRandom ) + { + %value ++; + if( %value > PE_ParticleEditor-->PEP_lifetimeMS_slider.getValue() ) + { + PE_ParticleEditor-->PEP_lifetimeMS_textEdit.setText( %value ); + PE_ParticleEditor-->PEP_lifetimeMS_slider.setValue( %value ); + } + } + else + { + %value --; + if( %value < PE_ParticleEditor-->PEP_lifetimeVarianceMS_slider.getValue() ) + { + PE_ParticleEditor-->PEP_lifetimeVarianceMS_textEdit.setText( %value ); + PE_ParticleEditor-->PEP_lifetimeVarianceMS_slider.setValue( %value ); + } + } + + // Submit undo. + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValueLifetimeMS = PE_ParticleEditor-->PEP_lifetimeMS_textEdit.getText(); + %last.newValueLifetimeVarianceMS = PE_ParticleEditor-->PEP_lifetimeVarianceMS_textEdit.getText(); + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveParticleLifeFields, "Update Active Particle"); + %action.particle = %particle; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + + %action.newValueLifetimeMS = PE_ParticleEditor-->PEP_lifetimeMS_textEdit.getText(); + %action.oldValueLifetimeMS = %particle.lifetimeMS; + + %action.newValueLifetimeVarianceMS = PE_ParticleEditor-->PEP_lifetimeVarianceMS_textEdit.getText(); + %action.oldValueLifetimeVarianceMS = %particle.lifetimeVarianceMS; + + ParticleEditor.submitUndo( %action ); + } + + %particle.lifetimeMS = PE_ParticleEditor-->PEP_lifetimeMS_textEdit.getText(); + %particle.lifetimeVarianceMS = PE_ParticleEditor-->PEP_lifetimeVarianceMS_textEdit.getText(); + %particle.reload(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::updateSpinFields( %this, %isMax, %value, %isSlider, %onMouseUp ) +{ + PE_ParticleEditor.setParticleDirty(); + %particle = PE_ParticleEditor.currParticle; + + // Transfer values over to gui controls. + if( %isMax ) + { + %value ++; + if( %value > PE_ParticleEditor-->PEP_spinRandomMax_slider.getValue() ) + { + PE_ParticleEditor-->PEP_spinRandomMax_textEdit.setText( %value ); + PE_ParticleEditor-->PEP_spinRandomMax_slider.setValue( %value ); + } + } + else + { + %value --; + if( %value < PE_ParticleEditor-->PEP_spinRandomMin_slider.getValue() ) + { + PE_ParticleEditor-->PEP_spinRandomMin_textEdit.setText( %value ); + PE_ParticleEditor-->PEP_spinRandomMin_slider.setValue( %value ); + } + } + + // Submit undo. + + %last = Editor.getUndoManager().getUndoAction(Editor.getUndoManager().getUndoCount() - 1); + if( (%isSlider) && (%last.isSlider) && (!%last.onMouseUp) ) + { + %last.isSlider = %isSlider; + %last.onMouseUp = %onMouseUp; + %last.newValueSpinRandomMax = PE_ParticleEditor-->PEP_spinRandomMax_textEdit.getText(); + %last.newValueSpinRandomMin = PE_ParticleEditor-->PEP_spinRandomMin_textEdit.getText(); + } + else + { + %action = ParticleEditor.createUndo(ActionUpdateActiveParticleSpinFields, "Update Active Particle"); + %action.particle = %particle; + %action.isSlider = %isSlider; + %action.onMouseUp = %onMouseUp; + + %action.newValueSpinRandomMax = PE_ParticleEditor-->PEP_spinRandomMax_textEdit.getText(); + %action.oldValueSpinRandomMax = %particle.spinRandomMax; + + %action.newValueSpinRandomMin = PE_ParticleEditor-->PEP_spinRandomMin_textEdit.getText(); + %action.oldValueSpinRandomMin = %particle.spinRandomMin; + + ParticleEditor.submitUndo( %action ); + } + + %particle.spinRandomMax = PE_ParticleEditor-->PEP_spinRandomMax_textEdit.getText(); + %particle.spinRandomMin = PE_ParticleEditor-->PEP_spinRandomMin_textEdit.getText(); + + %particle.reload(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::onNewParticle( %this ) +{ + // Bail if the user selected the same particle. + + %id = PEP_ParticleSelector.getSelected(); + if( %id == PE_ParticleEditor.currParticle ) + return; + + // Load new particle if we're not in a dirty state + if( PE_ParticleEditor.dirty ) + { + MessageBoxYesNoCancel("Save Existing Particle?", + "Do you want to save changes to <br><br>" @ PE_ParticleEditor.currParticle.getName(), + "PE_ParticleEditor.saveParticle(" @ PE_ParticleEditor.currParticle @ ");", + "PE_ParticleEditor.saveParticleDialogDontSave(" @ PE_ParticleEditor.currParticle @ "); PE_ParticleEditor.loadNewParticle();" + ); + } + else + { + PE_ParticleEditor.loadNewParticle(); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::loadNewParticle( %this, %particle ) +{ + if( isObject( %particle ) ) + %particle = %particle.getId(); + else + %particle = PEP_ParticleSelector.getSelected(); + + PE_ParticleEditor.currParticle = %particle; + + %particle.reload(); + + PE_ParticleEditor_NotDirtyParticle.assignFieldsFrom( %particle ); + PE_ParticleEditor_NotDirtyParticle.originalName = %particle.getName(); + + PE_ParticleEditor.guiSync(); + PE_ParticleEditor.setParticleNotDirty(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::setParticleDirty( %this ) +{ + PE_ParticleEditor.text = "Particle *"; + PE_ParticleEditor.dirty = true; + + %particle = PE_ParticleEditor.currParticle; + + if( %particle.getFilename() $= "" || %particle.getFilename() $= "tools/particleEditor/particleParticleEditor.ed.cs" ) + PE_ParticleSaver.setDirty( %particle, $PE_PARTICLEEDITOR_DEFAULT_FILENAME ); + else + PE_ParticleSaver.setDirty( %particle ); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::setParticleNotDirty( %this ) +{ + PE_ParticleEditor.text = "Particle"; + PE_ParticleEditor.dirty = false; + + PE_ParticleSaver.clearAll(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::showNewDialog( %this, %replaceSlot ) +{ + // Open a dialog if the current Particle is dirty + if( PE_ParticleEditor.dirty ) + { + MessageBoxYesNoCancel("Save Particle Changes?", + "Do you wish to save the changes made to the <br>current particle before changing the particle?", + "PE_ParticleEditor.saveParticle( " @ PE_ParticleEditor.currParticle.getName() @ " ); PE_ParticleEditor.createParticle( " @ %replaceSlot @ " );", + "PE_ParticleEditor.saveParticleDialogDontSave( " @ PE_ParticleEditor.currParticle.getName() @ " ); PE_ParticleEditor.createParticle( " @ %replaceSlot @ " );" + ); + } + else + { + PE_ParticleEditor.createParticle( %replaceSlot ); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::createParticle( %this, %replaceSlot ) +{ + // Make sure we have a spare slot on the current emitter. + + if( !%replaceSlot ) + { + %numExistingParticles = getWordCount( PE_EmitterEditor.currEmitter.particles ); + if( %numExistingParticles > 3 ) + { + MessageBoxOK( "Error", "An emitter cannot have more than 4 particles assigned to it." ); + return; + } + + %particleIndex = %numExistingParticles; + } + else + %particleIndex = %replaceSlot - 1; + + // Create the particle datablock and add to the emitter. + + %newParticle = getUniqueName( "newParticle" ); + + datablock ParticleData( %newParticle : DefaultParticle ) + { + }; + + // Submit undo. + + %action = ParticleEditor.createUndo( ActionCreateNewParticle, "Create New Particle" ); + %action.particle = %newParticle.getId(); + %action.particleIndex = %particleIndex; + %action.prevParticle = ( "PEE_EmitterParticleSelector" @ ( %particleIndex + 1 ) ).getSelected(); + %action.emitter = PE_EmitterEditor.currEmitter; + + ParticleEditor.submitUndo( %action ); + + // Execute action. + + %action.redo(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::showDeleteDialog( %this ) +{ + // Don't allow deleting DefaultParticle. + + if( PE_ParticleEditor.currParticle.getName() $= "DefaultParticle" ) + { + MessageBoxOK( "Error", "Cannot delete DefaultParticle"); + return; + } + + // Check to see if the particle emitter has more than 1 particle on it. + + if( getWordCount( PE_EmitterEditor.currEmitter.particles ) == 1 ) + { + MessageBoxOK( "Error", "At least one particle must remain on the particle emitter."); + return; + } + + // Bring up requester for confirmation. + + if( isObject( PE_ParticleEditor.currParticle ) ) + { + MessageBoxYesNoCancel( "Delete Particle?", + "Are you sure you want to delete<br><br>" @ PE_ParticleEditor.currParticle.getName() @ "<br><br> Particle deletion won't take affect until the engine is quit.", + "PE_ParticleEditor.saveParticleDialogDontSave( " @ PE_ParticleEditor.currParticle.getName() @ " ); PE_ParticleEditor.deleteParticle();", + "", + "" + ); + } +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::deleteParticle( %this ) +{ + %particle = PE_ParticleEditor.currParticle; + + // Submit undo. + + %action = ParticleEditor.createUndo( ActionDeleteParticle, "Delete Particle" ); + %action.particle = %particle; + %action.emitter = PE_EmitterEditor.currEmitter; + + ParticleEditor.submitUndo( %action ); + + // Execute action. + + %action.redo(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::saveParticle( %this, %particle ) +{ + %particle.setName( PEP_ParticleSelector.getText() ); + + PE_ParticleEditor_NotDirtyParticle.assignFieldsFrom( %particle ); + PE_ParticleEditor_NotDirtyParticle.originalName = %particle.getName(); + + PE_ParticleSaver.saveDirty(); + PE_ParticleEditor.setParticleNotDirty(); + + ParticleEditor.createParticleList(); +} + +//--------------------------------------------------------------------------------------------- + +function PE_ParticleEditor::saveParticleDialogDontSave( %this, %particle ) +{ + %particle.setName( PE_ParticleEditor_NotDirtyParticle.originalName ); + %particle.assignFieldsFrom( PE_ParticleEditor_NotDirtyParticle ); + + PE_ParticleEditor.setParticleNotDirty(); +} + +//============================================================================================= +// PE_ColorTintSwatch. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PE_ColorTintSwatch::updateParticleColor( %this, %color ) +{ + %arrayNum = %this.arrayNum; + + %r = getWord( %color, 0 ); + %g = getWord( %color, 1 ); + %b = getWord( %color, 2 ); + %a = getWord( %color, 3 ); + + %color = %r SPC %g SPC %b SPC %a; + %this.color = %color; + + PE_ParticleEditor.updateParticle( "colors[" @ %arrayNum @ "]", %color ); +} + +//============================================================================================= +// PEP_ParticleSelector_Control. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PEP_ParticleSelector_Control::onRenameItem( %this ) +{ + Parent::onRenameItem( %this ); + + //FIXME: need to check for validity of name and name clashes + + PE_ParticleEditor.setParticleDirty(); + + // Resort menu. + + %this-->PopupMenu.sort(); +} + +//============================================================================================= +// PEP_NewParticleButton. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function PEP_NewParticleButton::onDefaultClick( %this ) +{ + PE_ParticleEditor.showNewDialog(); +} + +//--------------------------------------------------------------------------------------------- + +function PEP_NewParticleButton::onCtrlClick( %this ) +{ + for( %i = 1; %i < 5; %i ++ ) + { + %popup = "PEE_EmitterParticleSelector" @ %i; + if( %popup.getSelected() == PEP_ParticleSelector.getSelected() ) + { + %replaceSlot = %i; + break; + } + } + + PE_ParticleEditor.showNewDialog( %replaceSlot ); +} diff --git a/Templates/BaseGame/game/tools/physicsTools/main.cs b/Templates/BaseGame/game/tools/physicsTools/main.cs new file mode 100644 index 000000000..8da40844e --- /dev/null +++ b/Templates/BaseGame/game/tools/physicsTools/main.cs @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +function physicsToggleSimulation() +{ + %isEnabled = physicsSimulationEnabled(); + if ( %isEnabled ) + { + physicsStateText.setText( "Simulation is paused." ); + physicsStopSimulation( "client" ); + physicsStopSimulation( "server" ); + } + else + { + physicsStateText.setText( "Simulation is unpaused." ); + physicsStartSimulation( "client" ); + physicsStartSimulation( "server" ); + } +} + +function initializePhysicsTools() +{ + echo( " % - Initializing Physics Tools" ); + + if ( !physicsPluginPresent() ) + { + echo( "No physics plugin exists." ); + return; + } + + globalactionmap.bindCmd( keyboard, "alt t", "physicsToggleSimulation();", "" ); + globalactionmap.bindCmd( keyboard, "alt r", "physicsRestoreState();", "" ); + + new ScriptObject( PhysicsEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = EWorldEditor; + }; +} + +function destroyPhysicsTools() +{ +} + +function PhysicsEditorPlugin::onWorldEditorStartup( %this ) +{ + new PopupMenu( PhysicsToolsMenu ) + { + superClass = "MenuBuilder"; + //class = "PhysXToolsMenu"; + + barTitle = "Physics"; + + item[0] = "Start Simulation" TAB "Ctrl-Alt P" TAB "physicsStartSimulation( \"client\" );physicsStartSimulation( \"server\" );"; + //item[1] = "Stop Simulation" TAB "" TAB "physicsSetTimeScale( 0 );"; + item[1] = "-"; + item[2] = "Speed 25%" TAB "" TAB "physicsSetTimeScale( 0.25 );"; + item[3] = "Speed 50%" TAB "" TAB "physicsSetTimeScale( 0.5 );"; + item[4] = "Speed 100%" TAB "" TAB "physicsSetTimeScale( 1.0 );"; + item[5] = "-"; + item[6] = "Reload NXBs" TAB "" TAB ""; + }; + + // Add our menu. + EditorGui.menuBar.insert( PhysicsToolsMenu, EditorGui.menuBar.dynamicItemInsertPos ); + + // Add ourselves to the window menu. + //EditorGui.addToWindowMenu( "Road and Path Editor", "", "RoadEditor" ); +} + +function PhysicsToolsMenu::onMenuSelect(%this) +{ + %isEnabled = physicsSimulationEnabled(); + + %itemText = !%isEnabled ? "Start Simulation" : "Pause Simulation"; + %itemCommand = !%isEnabled ? "physicsStartSimulation( \"client\" );physicsStartSimulation( \"server\" );" : "physicsStopSimulation( \"client\" );physicsStopSimulation( \"server\" );"; + + %this.setItemName( 0, %itemText ); + %this.setItemCommand( 0, %itemCommand ); +} + +function PhysicsEditorPlugin::onEditorWake( %this ) +{ + // Disable physics when entering + // the editor. Will be re-enabled + // when the editor is closed. + physicsStopSimulation( "client" ); + physicsStopSimulation( "server" ); + physicsRestoreState(); +} + +function PhysicsEditorPlugin::onEditorSleep( %this ) +{ + physicsStoreState(); + + %currentTimeScale = physicsGetTimeScale(); + if ( %currentTimeScale == 0.0 ) + physicsSetTimeScale( 1.0 ); + + physicsStartSimulation( "client" ); + physicsStartSimulation( "server" ); +} diff --git a/Templates/BaseGame/game/tools/resources/.gitignore b/Templates/BaseGame/game/tools/resources/.gitignore new file mode 100644 index 000000000..1bc0e838a --- /dev/null +++ b/Templates/BaseGame/game/tools/resources/.gitignore @@ -0,0 +1 @@ +# Keep directory in git repo diff --git a/Templates/BaseGame/game/tools/riverEditor/RiverEditorGui.gui b/Templates/BaseGame/game/tools/riverEditor/RiverEditorGui.gui new file mode 100644 index 000000000..1a6831489 --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/RiverEditorGui.gui @@ -0,0 +1,412 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiRiverEditorCtrl(RiverEditorGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "RiverEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + DefaultWidth = "10"; + HoverSplineColor = "0 255 0 255"; + SelectedSplineColor = "255 0 255 255"; + HoverNodeColor = "255 255 255 255"; + + new GuiWindowCollapseCtrl(RiverEditorTreeWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 167"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Rivers"; + + /* + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "LockSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "167 148"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.lockSelection(true); EditorTree.toggleLock();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "World Editor"; + hovertime = "1000"; + bitmap = "tools/gui/images/lock"; + buttonType = "ToggleButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "DeleteSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "185 148"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EditorMenuEditDelete();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "World Editor"; + hovertime = "1000"; + bitmap = "tools/gui/images/delete"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + + */ + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "5 25"; + Extent = "200 120"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 118"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(RiverTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + }; + }; + new GuiWindowCollapseCtrl(RiverEditorOptionsWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(RiverEditorTreeWindow.extent, 1) - 2; + Extent = "210 530"; + MinExtent = "210 300"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(){ //Node Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "202 85"; + Docking = "Top"; + Margin = "3 3 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "86 18"; + text = "Node Properties"; + }; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 21"; + Extent = "46 18"; + text = "Position"; + }; + new GuiTextEditCtrl(){ + internalName = "position"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 21"; + Extent = "141 18"; + text = ""; + AltCommand = "RiverEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 42"; + Extent = "46 18"; + text = "Rotation"; + }; + new GuiTextEditCtrl(){ + internalName = "rotation"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 42"; + Extent = "141 18"; + text = ""; + AltCommand = "RiverEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 63"; + Extent = "46 18"; + text = "Width"; + }; + new GuiTextEditCtrl(){ + internalName = "width"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "57 63"; + Extent = "52 18"; + text = ""; + AltCommand = "RiverEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "110 63"; + Extent = "32 18"; + text = "Depth"; + }; + new GuiTextEditCtrl(){ + internalName = "depth"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "146 63"; + Extent = "52 18"; + text = ""; + AltCommand = "RiverEditorGui.editNodeDetails();"; + }; + }; + new GuiContainer(){ //River Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 112"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "River Properties"; + }; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 129"; + Extent = "202 357"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "-14 41 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(RiverInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + name = "RiverInspector"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "1 1"; + Extent = "178 16"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + }; + }; + new GuiMLTextCtrl(RiverFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 485"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + +}; + +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/riverEditor/RiverEditorSettingsTab.gui b/Templates/BaseGame/game/tools/riverEditor/RiverEditorSettingsTab.gui new file mode 100644 index 000000000..553e2d7c6 --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/RiverEditorSettingsTab.gui @@ -0,0 +1,517 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(RiverEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(ERiverEditorSettingsPage) { + fitBook = "1"; + text = "River Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Defaults"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Width:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "RiverEditorPlugin.readSettings();"; + editorSettingsValue = "RiverEditor/DefaultWidth"; + editorSettingsWrite = "RiverEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Depth:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "RiverEditorPlugin.readSettings();"; + editorSettingsValue = "RiverEditor/DefaultDepth"; + editorSettingsWrite = "RiverEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Normal:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "RiverEditorPlugin.readSettings();"; + editorSettingsValue = "RiverEditor/DefaultNormal"; + editorSettingsWrite = "RiverEditorPlugin.writeSettings();"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Colors"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 10"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "RiverEditorPlugin.readSettings();"; + editorSettingsValue = "RiverEditor/HoverSplineColor"; + editorSettingsWrite = "RiverEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Hover Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 30"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "RiverEditorPlugin.readSettings();"; + editorSettingsValue = "RiverEditor/SelectedSplineColor"; + editorSettingsWrite = "RiverEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sel. Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/riverEditor/RiverEditorToolbar.gui b/Templates/BaseGame/game/tools/riverEditor/RiverEditorToolbar.gui new file mode 100644 index 000000000..f6e34b7c1 --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/RiverEditorToolbar.gui @@ -0,0 +1,323 @@ +%guiContent = new GuiControl(RiverEditorToolbar, EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 32"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 6"; + extent = "100 20"; + minExtent = "8 8"; + visible = "1"; + text = "River Settings"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiDynamicCtrlArrayControl(){ + Position = "83 3"; + extent = "111 32"; + colCount = "31"; + colSize = "29"; + rowCount = "1"; + RowSize = "27"; + rowSpacing = "2"; + colspacing = "4"; + + new GuiBitmapButtonCtrl(RiverEditorShowSplineBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "167 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$River::showSpline"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Spline"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-spline"; + groupNum = "7"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(RiverEditorWireframeBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "253 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$River::showWireframe"; + Command = "$River::showWireframe = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Wireframe"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-wireframe"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(RiverEditorShowRoadBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiDefalutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "89 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$River::showRiver"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show River Texture"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-texture"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiControl(RiverDefaultWidthTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "197 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "68 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Default Width"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "67 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.DefaultWidth = $ThisControl.getValue();"; + hovertime = "1000"; + text = "10"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "101 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(RiverDefaultWidthSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes Default River Width"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + new GuiControl(RiverDefaultDepthTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "327 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "68 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Default Depth"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "67 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.DefaultDepth = $ThisControl.getValue();"; + hovertime = "1000"; + text = "10"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "101 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(RiverDefaultDepthSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes Default River Depth"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; +}; +new GuiMouseEventCtrl(RiverDefaultWidthSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(RiverDefaultWidthTextEditContainer.position) + firstWord(RiverEditorToolbar.position) + 10 SPC + (getWord(RiverDefaultWidthTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "RiverDefaultWidthTextEditContainer-->textEdit.setText( mFloatLength($ThisControl.getValue(), 2)); RiverEditorGui.DefaultWidth = $ThisControl.getValue();"; + range = "0 100"; + ticks = "0"; + value = "10"; + }; +}; +new GuiMouseEventCtrl(RiverDefaultDepthSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(RiverDefaultDepthTextEditContainer.position) + firstWord(RiverEditorToolbar.position) + 10 SPC + (getWord(RiverDefaultDepthTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "RiverDefaultDepthTextEditContainer-->textEdit.setValue( mFloatLength($ThisControl.getValue(), 2)); RiverEditorGui.DefaultWidth = $ThisControl.getValue();"; + range = "0 100"; + ticks = "0"; + value = "10"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/riverEditor/main.cs b/Templates/BaseGame/game/tools/riverEditor/main.cs new file mode 100644 index 000000000..eafb3c3c8 --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/main.cs @@ -0,0 +1,230 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeRiverEditor() +{ + echo(" % - Initializing River Editor"); + + exec( "./riverEditor.cs" ); + exec( "./RiverEditorGui.gui" ); + exec( "./RiverEditorToolbar.gui" ); + exec( "./riverEditorGui.cs" ); + + // Add ourselves to EditorGui, where all the other tools reside + RiverEditorGui.setVisible( false ); + RiverEditorToolbar.setVisible(false); + RiverEditorOptionsWindow.setVisible( false ); + RiverEditorTreeWindow.setVisible( false ); + + EditorGui.add( RiverEditorGui ); + EditorGui.add( RiverEditorToolbar ); + EditorGui.add( RiverEditorOptionsWindow ); + EditorGui.add( RiverEditorTreeWindow ); + + new ScriptObject( RiverEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = RiverEditorGui; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "backspace", "RiverEditorGui.deleteNode();", "" ); + %map.bindCmd( keyboard, "1", "RiverEditorGui.prepSelectionMode();", "" ); + %map.bindCmd( keyboard, "2", "ToolsPaletteArray->RiverEditorMoveMode.performClick();", "" ); + %map.bindCmd( keyboard, "3", "ToolsPaletteArray->RiverEditorRotateMode.performClick();", "" ); + %map.bindCmd( keyboard, "4", "ToolsPaletteArray->RiverEditorScaleMode.performClick();", "" ); + %map.bindCmd( keyboard, "5", "ToolsPaletteArray->RiverEditorAddRiverMode.performClick();", "" ); + %map.bindCmd( keyboard, "=", "ToolsPaletteArray->RiverEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadadd", "ToolsPaletteArray->RiverEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "-", "ToolsPaletteArray->RiverEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadminus", "ToolsPaletteArray->RiverEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "z", "RiverEditorShowSplineBtn.performClick();", "" ); + %map.bindCmd( keyboard, "x", "RiverEditorWireframeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "v", "RiverEditorShowRoadBtn.performClick();", "" ); + RiverEditorPlugin.map = %map; + + RiverEditorPlugin.initSettings(); +} + +function destroyRiverEditor() +{ +} + +function RiverEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "River Editor", "", RiverEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "River Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "RiverEditorPlugin", "RiverEditorPalette", expandFilename("tools/worldEditor/images/toolbar/river-editor"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( RiverEditorOptionsWindow, RiverEditorTreeWindow); + + // Add ourselves to the Editor Settings window + exec( "./RiverEditorSettingsTab.gui" ); + ESettingsWindow.addTabPage( ERiverEditorSettingsPage ); +} + +function RiverEditorPlugin::onActivated( %this ) +{ + %this.readSettings(); + + $River::EditorOpen = true; + + ToolsPaletteArray->RiverEditorAddRiverMode.performClick(); + EditorGui.bringToFront( RiverEditorGui ); + + RiverEditorGui.setVisible(true); + RiverEditorGui.makeFirstResponder( true ); + RiverEditorToolbar.setVisible(true); + + RiverEditorOptionsWindow.setVisible( true ); + RiverEditorTreeWindow.setVisible( true ); + + RiverTreeView.open(ServerRiverSet,true); + %this.map.push(); + + // Store this on a dynamic field + // in order to restore whatever setting + // the user had before. + %this.prevGizmoAlignment = GlobalGizmoProfile.alignment; + + // The DecalEditor always uses Object alignment. + GlobalGizmoProfile.alignment = "Object"; + + // Set the status bar here until all tool have been hooked up + EditorGuiStatusBar.setInfo("River editor."); + EditorGuiStatusBar.setSelection(""); + + // Allow the Gui to setup. + RiverEditorGui.onEditorActivated(); + + Parent::onActivated(%this); +} + +function RiverEditorPlugin::onDeactivated( %this ) +{ + %this.writeSettings(); + + $River::EditorOpen = false; + + RiverEditorGui.setVisible(false); + RiverEditorToolbar.setVisible(false); + RiverEditorOptionsWindow.setVisible( false ); + RiverEditorTreeWindow.setVisible( false ); + %this.map.pop(); + + // Restore the previous Gizmo + // alignment settings. + GlobalGizmoProfile.alignment = %this.prevGizmoAlignment; + + // Allow the Gui to cleanup. + RiverEditorGui.onEditorDeactivated(); + + Parent::onDeactivated(%this); +} + +function RiverEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %hasSelection = false; + + if( isObject( RiverEditorGui.river ) ) + %hasSelection = true; + + %editMenu.enableItem( 3, false ); // Cut + %editMenu.enableItem( 4, false ); // Copy + %editMenu.enableItem( 5, false ); // Paste + %editMenu.enableItem( 6, %hasSelection ); // Delete + %editMenu.enableItem( 8, false ); // Deselect +} + +function RiverEditorPlugin::handleDelete( %this ) +{ + RiverEditorGui.deleteNode(); +} + +function RiverEditorPlugin::handleEscape( %this ) +{ + return RiverEditorGui.onEscapePressed(); +} + +function RiverEditorPlugin::isDirty( %this ) +{ + return RiverEditorGui.isDirty; +} + +function RiverEditorPlugin::onSaveMission( %this, %missionFile ) +{ + if( RiverEditorGui.isDirty ) + { + MissionGroup.save( %missionFile ); + RiverEditorGui.isDirty = false; + } +} + +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function RiverEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "RiverEditor", true ); + + EditorSettings.setDefaultValue( "DefaultWidth", "10" ); + EditorSettings.setDefaultValue( "DefaultDepth", "5" ); + EditorSettings.setDefaultValue( "DefaultNormal", "0 0 1" ); + EditorSettings.setDefaultValue( "HoverSplineColor", "255 0 0 255" ); + EditorSettings.setDefaultValue( "SelectedSplineColor", "0 255 0 255" ); + EditorSettings.setDefaultValue( "HoverNodeColor", "255 255 255 255" ); //<-- Not currently used + + EditorSettings.endGroup(); +} + +function RiverEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "RiverEditor", true ); + + RiverEditorGui.DefaultWidth = EditorSettings.value("DefaultWidth"); + RiverEditorGui.DefaultDepth = EditorSettings.value("DefaultDepth"); + RiverEditorGui.DefaultNormal = EditorSettings.value("DefaultNormal"); + RiverEditorGui.HoverSplineColor = EditorSettings.value("HoverSplineColor"); + RiverEditorGui.SelectedSplineColor = EditorSettings.value("SelectedSplineColor"); + RiverEditorGui.HoverNodeColor = EditorSettings.value("HoverNodeColor"); + + EditorSettings.endGroup(); +} + +function RiverEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "RiverEditor", true ); + + EditorSettings.setValue( "DefaultWidth", RiverEditorGui.DefaultWidth ); + EditorSettings.setValue( "DefaultDepth", RiverEditorGui.DefaultDepth ); + EditorSettings.setValue( "DefaultNormal", RiverEditorGui.DefaultNormal ); + EditorSettings.setValue( "HoverSplineColor", RiverEditorGui.HoverSplineColor ); + EditorSettings.setValue( "SelectedSplineColor", RiverEditorGui.SelectedSplineColor ); + EditorSettings.setValue( "HoverNodeColor", RiverEditorGui.HoverNodeColor ); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/riverEditor/riverEditor.cs b/Templates/BaseGame/game/tools/riverEditor/riverEditor.cs new file mode 100644 index 000000000..83da8b85c --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/riverEditor.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile( RiverEditorProfile ) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192 192"; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiSimpleBorderProfile) +{ + opaque = false; + border = 1; + category = "Editor"; +}; + \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/riverEditor/riverEditorGui.cs b/Templates/BaseGame/game/tools/riverEditor/riverEditorGui.cs new file mode 100644 index 000000000..af88e1c1f --- /dev/null +++ b/Templates/BaseGame/game/tools/riverEditor/riverEditorGui.cs @@ -0,0 +1,261 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$River::EditorOpen = false; +$River::wireframe = true; +$River::showSpline = true; +$River::showRiver = true; +$River::showWalls = true; + +function RiverEditorGui::onEditorActivated( %this ) +{ + %count = EWorldEditor.getSelectionSize(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = EWorldEditor.getSelectedObject(%i); + if ( %obj.getClassName() !$= "River" ) + EWorldEditor.unselectObject( %obj ); + else + %this.setSelectedRiver( %obj ); + } + + %this.onRiverSelected( %this.getSelectedRiver() ); + %this.onNodeSelected(-1); +} + +function RiverEditorGui::onEditorDeactivated( %this ) +{ +} + +function RiverEditorGui::createRiver( %this ) +{ + %river = new River() + { + rippleDir[0] = "0.000000 1.000000"; + rippleDir[1] = "0.707000 0.707000"; + rippleDir[2] = "0.500000 0.860000"; + + rippleSpeed[0] = "-0.065"; + rippleSpeed[1] = "0.09"; + rippleSpeed[2] = "0.04"; + + rippleTexScale[0] = "7.140000 7.140000"; + rippleTexScale[1] = "6.250000 12.500000"; + rippleTexScale[2] = "50.000000 50.000000"; + + waveDir[0] = "0.000000 1.000000"; + waveDir[1] = "0.707000 0.707000"; + waveDir[2] = "0.500000 0.860000"; + + waveSpeed[0] = "1"; + waveSpeed[1] = "1"; + waveSpeed[2] = "1"; + + waveMagnitude[0] = "0.2"; + waveMagnitude[1] = "0.2"; + waveMagnitude[2] = "0.2"; + + baseColor = "45 108 171 255"; + + rippleTex = "art/water/ripple.dds"; + foamTex = "art/water/foam"; + depthGradientTex = "art/water/depthcolor_ramp"; + }; + + return %river; +} + +function RiverEditorGui::paletteSync( %this, %mode ) +{ + %evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);"; + eval(%evalShortcut); +} + +function RiverEditorGui::onEscapePressed( %this ) +{ + if( %this.getMode() $= "RiverEditorAddNodeMode" ) + { + %this.prepSelectionMode(); + return true; + } + return false; +} + +function RiverEditorGui::onRiverSelected( %this, %river ) +{ + %this.river = %river; + RiverInspector.inspect( %river ); + RiverTreeView.buildVisibleTree(true); + if( RiverTreeView.getSelectedObject() != %river ) + { + RiverTreeView.clearSelection(); + %treeId = RiverTreeView.findItemByObjectId( %river ); + RiverTreeView.selectItem( %treeId ); + } +} + +function RiverEditorGui::onNodeSelected( %this, %nodeIdx ) +{ + if ( %nodeIdx == -1 ) + { + RiverEditorOptionsWindow-->position.setActive( false ); + RiverEditorOptionsWindow-->position.setValue( "" ); + + RiverEditorOptionsWindow-->rotation.setActive( false ); + RiverEditorOptionsWindow-->rotation.setValue( "" ); + + RiverEditorOptionsWindow-->width.setActive( false ); + RiverEditorOptionsWindow-->width.setValue( "" ); + + RiverEditorOptionsWindow-->depth.setActive( false ); + RiverEditorOptionsWindow-->depth.setValue( "" ); + } + else + { + RiverEditorOptionsWindow-->position.setActive( true ); + RiverEditorOptionsWindow-->position.setValue( %this.getNodePosition() ); + + RiverEditorOptionsWindow-->rotation.setActive( true ); + RiverEditorOptionsWindow-->rotation.setValue( %this.getNodeNormal() ); + + RiverEditorOptionsWindow-->width.setActive( true ); + RiverEditorOptionsWindow-->width.setValue( %this.getNodeWidth() ); + + RiverEditorOptionsWindow-->depth.setActive( true ); + RiverEditorOptionsWindow-->depth.setValue( %this.getNodeDepth() ); + } +} + +function RiverEditorGui::onNodeModified( %this, %nodeIdx ) +{ + RiverEditorOptionsWindow-->position.setValue( %this.getNodePosition() ); + RiverEditorOptionsWindow-->rotation.setValue( %this.getNodeNormal() ); + RiverEditorOptionsWindow-->width.setValue( %this.getNodeWidth() ); + RiverEditorOptionsWindow-->depth.setValue( %this.getNodeDepth() ); +} + +function RiverEditorGui::editNodeDetails( %this ) +{ + + %this.setNodePosition( RiverEditorOptionsWindow-->position.getText() ); + %this.setNodeNormal( RiverEditorOptionsWindow-->rotation.getText() ); + %this.setNodeWidth( RiverEditorOptionsWindow-->width.getText() ); + %this.setNodeDepth( RiverEditorOptionsWindow-->depth.getText() ); +} + +function RiverInspector::inspect( %this, %obj ) +{ + %name = ""; + if ( isObject( %obj ) ) + %name = %obj.getName(); + else + RiverFieldInfoControl.setText( "" ); + + //RiverInspectorNameEdit.setValue( %name ); + Parent::inspect( %this, %obj ); +} + +function RiverInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); +} + +function RiverInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + RiverFieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +function RiverTreeView::onInspect(%this, %obj) +{ + RiverInspector.inspect(%obj); +} + +function RiverTreeView::onSelect(%this, %obj) +{ + RiverEditorGui.road = %obj; + RiverInspector.inspect( %obj ); + if(%obj != RiverEditorGui.getSelectedRiver()) + { + RiverEditorGui.setSelectedRiver( %obj ); + } +} + +function RiverEditorGui::prepSelectionMode( %this ) +{ + %mode = %this.getMode(); + + if ( %mode $= "RiverEditorAddNodeMode" ) + { + if ( isObject( %this.getSelectedRiver() ) ) + %this.deleteNode(); + } + + %this.setMode( "RiverEditorSelectMode" ); + ToolsPaletteArray-->RiverEditorSelectMode.setStateOn(1); +} + +//------------------------------------------------------------------------------ +function ERiverEditorSelectModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorAddModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorMoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorRotateModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorScaleModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorInsertModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERiverEditorRemoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function RiverDefaultWidthSliderCtrlContainer::onWake(%this) +{ + RiverDefaultWidthSliderCtrlContainer-->slider.setValue(RiverDefaultWidthTextEditContainer-->textEdit.getText()); +} + +function RiverDefaultDepthSliderCtrlContainer::onWake(%this) +{ + RiverDefaultDepthSliderCtrlContainer-->slider.setValue(RiverDefaultDepthTextEditContainer-->textEdit.getText()); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/roadEditor/RoadEditorGui.gui b/Templates/BaseGame/game/tools/roadEditor/RoadEditorGui.gui new file mode 100644 index 000000000..7f9eba0f6 --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/RoadEditorGui.gui @@ -0,0 +1,386 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiRoadEditorCtrl(RoadEditorGui) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "RoadEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + DefaultWidth = "10"; + HoverSplineColor = "0 255 0 255"; + SelectedSplineColor = "255 0 255 255"; + HoverNodeColor = "255 255 255 255"; + + new GuiWindowCollapseCtrl(RoadEditorTreeWindow) { + internalName = ""; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 167"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Roads & Paths"; + + /* + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "LockSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "167 148"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.lockSelection(true); EditorTree.toggleLock();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "World Editor"; + hovertime = "1000"; + bitmap = "tools/gui/images/lock"; + buttonType = "ToggleButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "DeleteSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "185 148"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EditorMenuEditDelete();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "World Editor"; + hovertime = "1000"; + bitmap = "tools/gui/images/delete"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + */ + new GuiContainer(){ + profile = "ToolsGuiDefaultProfile"; + Position = "5 25"; + Extent = "200 120"; + Docking = "Client"; + Margin = "3 1 3 3 "; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 118"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(RoadTreeView) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + }; + }; + new GuiWindowCollapseCtrl(RoadEditorOptionsWindow) { + internalName = "Window"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 SPC + getWord(EditorGuiToolbar.extent, 1) + getWord(RoadEditorTreeWindow.extent, 1) - 2; + Extent = "210 530"; + MinExtent = "210 298"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + + new GuiContainer(RoadEditorProperties){ //Node Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "202 64"; + Docking = "Top"; + Margin = "3 3 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "86 18"; + text = "Node Properties"; + }; + + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 21"; + Extent = "46 18"; + text = "Position"; + }; + new GuiTextEditCtrl(){ + internalName = "position"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 21"; + Extent = "141 18"; + text = ""; + AltCommand = "RoadEditorGui.editNodeDetails();"; + }; + new GuiTextCtrl(){ + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 42"; + Extent = "46 18"; + text = "Width"; + }; + new GuiTextEditCtrl(){ + internalName = "width"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "57 42"; + Extent = "141 18"; + text = ""; + AltCommand = "RoadEditorGui.editNodeDetails();"; + }; + }; + new GuiContainer(){ //Decal Road Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 91"; + Extent = "202 31"; + Docking = "Top"; + Margin = "0 0 3 3"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "121 18"; + text = "Decal Road Properties"; + }; + }; + + new GuiContainer(){ + profile = ToolsGuiDefaultProfile; + Position = "4 108"; + Extent = "202 377"; + HorizSizing = "width"; + VertSizing = "height"; + isContainer = "1"; + Docking = "Client"; + Margin = "-14 41 3 3"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 377"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(RoadInspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "200 196"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + groupFilters = "+SimBase,+DecalRoad"; + }; + }; + }; + new GuiMLTextCtrl(RoadFieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 485"; + Extent = "202 42"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; + +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/roadEditor/RoadEditorSettingsTab.gui b/Templates/BaseGame/game/tools/roadEditor/RoadEditorSettingsTab.gui new file mode 100644 index 000000000..b37485cda --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/RoadEditorSettingsTab.gui @@ -0,0 +1,457 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(RoadEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(ERoadEditorSettingsPage) { + fitBook = "1"; + text = "Road Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Defaults"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Width:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "RoadEditorPlugin.readSettings();"; + editorSettingsValue = "RoadEditor/DefaultWidth"; + editorSettingsWrite = "RoadEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Material:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "RoadEditorPlugin.readSettings();"; + editorSettingsValue = "RoadEditor/MaterialName"; + editorSettingsWrite = "RoadEditorPlugin.writeSettings();"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Colors"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 10"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "RoadEditorPlugin.readSettings();"; + editorSettingsValue = "RoadEditor/HoverSplineColor"; + editorSettingsWrite = "RoadEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Hover Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 30"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "RoadEditorPlugin.readSettings();"; + editorSettingsValue = "RoadEditor/SelectedSplineColor"; + editorSettingsWrite = "RoadEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sel. Spline:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/roadEditor/RoadEditorToolbar.gui b/Templates/BaseGame/game/tools/roadEditor/RoadEditorToolbar.gui new file mode 100644 index 000000000..01171bf0b --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/RoadEditorToolbar.gui @@ -0,0 +1,273 @@ +%guiContent = new GuiControl(RoadEditorToolbar) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 32"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 6"; + extent = "70 20"; + minExtent = "8 8"; + visible = "1"; + text = "Road Settings"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiDynamicCtrlArrayControl(){ + Position = "83 3"; + extent = "111 32"; + colCount = "31"; + colSize = "29"; + rowCount = "1"; + RowSize = "27"; + rowSpacing = "2"; + colspacing = "4"; + + new GuiBitmapButtonCtrl(RoadEditorShowSplineBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "167 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$DecalRoad::showSpline"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Spline"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-spline"; + groupNum = "7"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(RoadEditorWireframeBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "253 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$DecalRoad::wireframe"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Wireframe"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-wireframe"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapButtonCtrl(RoadEditorShowRoadBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiDefalutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "89 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Variable = "$DecalRoad::showRoad"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + toolTip = "Show Road Texture"; + bitmap = "tools/worldEditor/images/road-river/menubar/show-texture"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiControl(RoadDefaultWidthTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "197 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "68 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Default Width"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "67 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.DefaultWidth = $ThisControl.getValue();"; + hovertime = "1000"; + text = "10"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "101 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(RoadDefaultWidthSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes Default Road Width"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + /*new GuiTextEditSliderCtrl(RoadEditorDefaultWidthSlider) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "429 6"; + Extent = "46 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "RoadEditorGui.DefaultWidth = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "10.0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + format = "%2.1f"; + range = "0 100"; + increment = "0.5"; + focusOnMouseWheel = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "347 7"; + Extent = "66 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "Default Width"; + maxLength = "1024"; + };*/ +}; +new GuiMouseEventCtrl(RoadDefaultWidthSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(RoadDefaultWidthTextEditContainer.position) + firstWord(RoadEditorToolbar.position) + 10 SPC + (getWord(RoadDefaultWidthTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "RoadDefaultWidthTextEditContainer-->textEdit.setText( mFloatLength($ThisControl.getValue(), 2)); RoadEditorGui.DefaultWidth = $ThisControl.getValue();"; + range = "0 100"; + ticks = "0"; + value = "10"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/roadEditor/main.cs b/Templates/BaseGame/game/tools/roadEditor/main.cs new file mode 100644 index 000000000..f45823670 --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/main.cs @@ -0,0 +1,215 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeRoadEditor() +{ + echo( " - Initializing Road and Path Editor" ); + + exec( "./roadEditor.cs" ); + exec( "./RoadEditorGui.gui" ); + exec( "./RoadEditorToolbar.gui"); + exec( "./roadEditorGui.cs" ); + + // Add ourselves to EditorGui, where all the other tools reside + RoadEditorGui.setVisible( false ); + RoadEditorToolbar.setVisible( false ); + RoadEditorOptionsWindow.setVisible( false ); + RoadEditorTreeWindow.setVisible( false ); + + EditorGui.add( RoadEditorGui ); + EditorGui.add( RoadEditorToolbar ); + EditorGui.add( RoadEditorOptionsWindow ); + EditorGui.add( RoadEditorTreeWindow ); + + new ScriptObject( RoadEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = RoadEditorGui; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "backspace", "RoadEditorGui.onDeleteKey();", "" ); + %map.bindCmd( keyboard, "1", "RoadEditorGui.prepSelectionMode();", "" ); + %map.bindCmd( keyboard, "2", "ToolsPaletteArray->RoadEditorMoveMode.performClick();", "" ); + %map.bindCmd( keyboard, "4", "ToolsPaletteArray->RoadEditorScaleMode.performClick();", "" ); + %map.bindCmd( keyboard, "5", "ToolsPaletteArray->RoadEditorAddRoadMode.performClick();", "" ); + %map.bindCmd( keyboard, "=", "ToolsPaletteArray->RoadEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadadd", "ToolsPaletteArray->RoadEditorInsertPointMode.performClick();", "" ); + %map.bindCmd( keyboard, "-", "ToolsPaletteArray->RoadEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "numpadminus", "ToolsPaletteArray->RoadEditorRemovePointMode.performClick();", "" ); + %map.bindCmd( keyboard, "z", "RoadEditorShowSplineBtn.performClick();", "" ); + %map.bindCmd( keyboard, "x", "RoadEditorWireframeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "v", "RoadEditorShowRoadBtn.performClick();", "" ); + RoadEditorPlugin.map = %map; + + RoadEditorPlugin.initSettings(); +} + +function destroyRoadEditor() +{ +} + +function RoadEditorPlugin::onWorldEditorStartup( %this ) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Road and Path Editor", "", RoadEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Road Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "RoadEditorPlugin", "RoadEditorPalette", expandFilename("tools/worldEditor/images/toolbar/road-path-editor"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( RoadEditorOptionsWindow, RoadEditorTreeWindow); + + // Add ourselves to the Editor Settings window + exec( "./RoadEditorSettingsTab.gui" ); + ESettingsWindow.addTabPage( ERoadEditorSettingsPage ); +} + +function RoadEditorPlugin::onActivated( %this ) +{ + %this.readSettings(); + + ToolsPaletteArray->RoadEditorAddRoadMode.performClick(); + EditorGui.bringToFront( RoadEditorGui ); + + RoadEditorGui.setVisible( true ); + RoadEditorGui.makeFirstResponder( true ); + RoadEditorToolbar.setVisible( true ); + + RoadEditorOptionsWindow.setVisible( true ); + RoadEditorTreeWindow.setVisible( true ); + + RoadTreeView.open(ServerDecalRoadSet,true); + + %this.map.push(); + + // Set the status bar here until all tool have been hooked up + EditorGuiStatusBar.setInfo("Road editor."); + EditorGuiStatusBar.setSelection(""); + + Parent::onActivated(%this); +} + +function RoadEditorPlugin::onDeactivated( %this ) +{ + %this.writeSettings(); + + RoadEditorGui.setVisible( false ); + RoadEditorToolbar.setVisible( false ); + RoadEditorOptionsWindow.setVisible( false ); + RoadEditorTreeWindow.setVisible( false ); + %this.map.pop(); + + Parent::onDeactivated(%this); +} + +function RoadEditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %hasSelection = false; + + if( isObject( RoadEditorGui.road ) ) + %hasSelection = true; + + %editMenu.enableItem( 3, false ); // Cut + %editMenu.enableItem( 4, false ); // Copy + %editMenu.enableItem( 5, false ); // Paste + %editMenu.enableItem( 6, %hasSelection ); // Delete + %editMenu.enableItem( 8, false ); // Deselect +} + +function RoadEditorPlugin::handleDelete( %this ) +{ + RoadEditorGui.onDeleteKey(); +} + +function RoadEditorPlugin::handleEscape( %this ) +{ + return RoadEditorGui.onEscapePressed(); +} + +function RoadEditorPlugin::isDirty( %this ) +{ + return RoadEditorGui.isDirty; +} + +function RoadEditorPlugin::onSaveMission( %this, %missionFile ) +{ + if( RoadEditorGui.isDirty ) + { + MissionGroup.save( %missionFile ); + RoadEditorGui.isDirty = false; + } +} + +function RoadEditorPlugin::setEditorFunction( %this ) +{ + %terrainExists = parseMissionGroup( "TerrainBlock" ); + + if( %terrainExists == false ) + MessageBoxYesNoCancel("No Terrain","Would you like to create a New Terrain?", "Canvas.pushDialog(CreateNewTerrainGui);"); + + return %terrainExists; +} + +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function RoadEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "RoadEditor", true ); + + EditorSettings.setDefaultValue( "DefaultWidth", "10" ); + EditorSettings.setDefaultValue( "HoverSplineColor", "255 0 0 255" ); + EditorSettings.setDefaultValue( "SelectedSplineColor", "0 255 0 255" ); + EditorSettings.setDefaultValue( "HoverNodeColor", "255 255 255 255" ); //<-- Not currently used + EditorSettings.setDefaultValue( "MaterialName", "DefaultDecalRoadMaterial" ); + + EditorSettings.endGroup(); +} + +function RoadEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "RoadEditor", true ); + + RoadEditorGui.DefaultWidth = EditorSettings.value("DefaultWidth"); + RoadEditorGui.HoverSplineColor = EditorSettings.value("HoverSplineColor"); + RoadEditorGui.SelectedSplineColor = EditorSettings.value("SelectedSplineColor"); + RoadEditorGui.HoverNodeColor = EditorSettings.value("HoverNodeColor"); + RoadEditorGui.materialName = EditorSettings.value("MaterialName"); + + EditorSettings.endGroup(); +} + +function RoadEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "RoadEditor", true ); + + EditorSettings.setValue( "DefaultWidth", RoadEditorGui.DefaultWidth ); + EditorSettings.setValue( "HoverSplineColor", RoadEditorGui.HoverSplineColor ); + EditorSettings.setValue( "SelectedSplineColor", RoadEditorGui.SelectedSplineColor ); + EditorSettings.setValue( "HoverNodeColor", RoadEditorGui.HoverNodeColor ); + EditorSettings.setValue( "MaterialName", RoadEditorGui.materialName ); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/roadEditor/roadEditor.cs b/Templates/BaseGame/game/tools/roadEditor/roadEditor.cs new file mode 100644 index 000000000..0822050b0 --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/roadEditor.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile( RoadEditorProfile ) +{ + canKeyFocus = true; + opaque = true; + fillColor = "192 192 192 192"; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiSimpleBorderProfile) +{ + opaque = false; + border = 1; + category = "Editor"; +}; + +singleton GuiCursor(RoadEditorMoveCursor) +{ + hotSpot = "4 4"; + renderOffset = "0 0"; + bitmapName = "~/gui/images/macCursor"; + category = "Editor"; +}; + +singleton GuiCursor( RoadEditorMoveNodeCursor ) +{ + hotSpot = "1 1"; + renderOffset = "0 0"; + bitmapName = "./Cursors/outline/drag_node_outline"; + category = "Editor"; +}; + +singleton GuiCursor( RoadEditorAddNodeCursor ) +{ + hotSpot = "1 1"; + renderOffset = "0 0"; + bitmapName = "./Cursors/outline/add_to_end_outline"; + category = "Editor"; +}; + +singleton GuiCursor( RoadEditorInsertNodeCursor ) +{ + hotSpot = "1 1"; + renderOffset = "0 0"; + bitmapName = "./Cursors/outline/insert_in_middle_outline"; + category = "Editor"; +}; + +singleton GuiCursor( RoadEditorResizeNodeCursor ) +{ + hotSpot = "1 1"; + renderOffset = "0 0"; + bitmapName = "./Cursors/outline/widen_path_outline"; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/roadEditor/roadEditorGui.cs b/Templates/BaseGame/game/tools/roadEditor/roadEditorGui.cs new file mode 100644 index 000000000..5c6a29b40 --- /dev/null +++ b/Templates/BaseGame/game/tools/roadEditor/roadEditorGui.cs @@ -0,0 +1,249 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function RoadEditorGui::onWake( %this ) +{ + $DecalRoad::EditorOpen = true; + + %count = EWorldEditor.getSelectionSize(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = EWorldEditor.getSelectedObject(%i); + if ( %obj.getClassName() !$= "DecalRoad" ) + EWorldEditor.unselectObject(); + else + %this.setSelectedRoad( %obj ); + } + + %this.onNodeSelected(-1); +} + +function RoadEditorGui::onSleep( %this ) +{ + $DecalRoad::EditorOpen = false; +} + +function RoadEditorGui::paletteSync( %this, %mode ) +{ + %evalShortcut = "ToolsPaletteArray-->" @ %mode @ ".setStateOn(1);"; + eval(%evalShortcut); +} + +function RoadEditorGui::onDeleteKey( %this ) +{ + %road = %this.getSelectedRoad(); + %node = %this.getSelectedNode(); + + if ( !isObject( %road ) ) + return; + + if ( %node != -1 ) + { + %this.deleteNode(); + } + else + { + MessageBoxOKCancel( "Notice", "Delete selected DecalRoad?", "RoadEditorGui.deleteRoad();", "" ); + } +} + +function RoadEditorGui::onEscapePressed( %this ) +{ + if( %this.getMode() $= "RoadEditorAddNodeMode" ) + { + %this.prepSelectionMode(); + return true; + } + return false; +} + +//just in case we need it later +function RoadEditorGui::onRoadCreation( %this ) +{ +} + +function RoadEditorGui::onRoadSelected( %this, %road ) +{ + %this.road = %road; + + // Update the materialEditorList + if(isObject( %road )) + $Tools::materialEditorList = %road.getId(); + + RoadInspector.inspect( %road ); + RoadTreeView.buildVisibleTree(true); + if( RoadTreeView.getSelectedObject() != %road ) + { + RoadTreeView.clearSelection(); + %treeId = RoadTreeView.findItemByObjectId( %road ); + RoadTreeView.selectItem( %treeId ); + } +} + +function RoadEditorGui::onNodeSelected( %this, %nodeIdx ) +{ + + if ( %nodeIdx == -1 ) + { + RoadEditorProperties-->position.setActive( false ); + RoadEditorProperties-->position.setValue( "" ); + + RoadEditorProperties-->width.setActive( false ); + RoadEditorProperties-->width.setValue( "" ); + } + else + { + RoadEditorProperties-->position.setActive( true ); + RoadEditorProperties-->position.setValue( %this.getNodePosition() ); + + RoadEditorProperties-->width.setActive( true ); + RoadEditorProperties-->width.setValue( %this.getNodeWidth() ); + } + +} + +function RoadEditorGui::onNodeModified( %this, %nodeIdx ) +{ + + RoadEditorProperties-->position.setValue( %this.getNodePosition() ); + RoadEditorProperties-->width.setValue( %this.getNodeWidth() ); + +} + +function RoadEditorGui::editNodeDetails( %this ) +{ + + %this.setNodePosition( RoadEditorProperties-->position.getText() ); + %this.setNodeWidth( RoadEditorProperties-->width.getText() ); +} + +function RoadEditorGui::onBrowseClicked( %this ) +{ + //%filename = RETextureFileCtrl.getText(); + + %dlg = new OpenFileDialog() + { + Filters = "All Files (*.*)|*.*|"; + DefaultPath = RoadEditorGui.lastPath; + DefaultFile = %filename; + ChangePath = false; + MustExist = true; + }; + + %ret = %dlg.Execute(); + if(%ret) + { + RoadEditorGui.lastPath = filePath( %dlg.FileName ); + %filename = %dlg.FileName; + RoadEditorGui.setTextureFile( %filename ); + RETextureFileCtrl.setText( %filename ); + } + + %dlg.delete(); +} + +function RoadInspector::inspect( %this, %obj ) +{ + %name = ""; + if ( isObject( %obj ) ) + %name = %obj.getName(); + else + RoadFieldInfoControl.setText( "" ); + + //RoadInspectorNameEdit.setValue( %name ); + Parent::inspect( %this, %obj ); +} + +function RoadInspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // Same work to do as for the regular WorldEditor Inspector. + Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ); +} + +function RoadInspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + RoadFieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +function RoadTreeView::onInspect(%this, %obj) +{ + RoadInspector.inspect(%obj); +} + +function RoadTreeView::onSelect(%this, %obj) +{ + RoadEditorGui.road = %obj; + RoadInspector.inspect( %obj ); + if(%obj != RoadEditorGui.getSelectedRoad()) + { + RoadEditorGui.setSelectedRoad( %obj ); + } +} + +function RoadEditorGui::prepSelectionMode( %this ) +{ + %mode = %this.getMode(); + + if ( %mode $= "RoadEditorAddNodeMode" ) + { + if ( isObject( %this.getSelectedRoad() ) ) + %this.deleteNode(); + } + + %this.setMode( "RoadEditorSelectMode" ); + ToolsPaletteArray-->RoadEditorSelectMode.setStateOn(1); +} +//------------------------------------------------------------------------------ +function ERoadEditorSelectModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERoadEditorAddModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERoadEditorMoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERoadEditorScaleModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERoadEditorInsertModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function ERoadEditorRemoveModeBtn::onClick(%this) +{ + EditorGuiStatusBar.setInfo(%this.ToolTip); +} + +function RoadDefaultWidthSliderCtrlContainer::onWake(%this) +{ + RoadDefaultWidthSliderCtrlContainer-->slider.setValue(RoadDefaultWidthTextEditContainer-->textEdit.getText()); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/settings.xml b/Templates/BaseGame/game/tools/settings.xml new file mode 100644 index 000000000..739530e7b --- /dev/null +++ b/Templates/BaseGame/game/tools/settings.xml @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<EditorSettings> + <Group name="GuiEditor"> + <Setting name="previewResolution">1280 1024</Setting> + <Setting name="lastPath">E:/gamedev/T3DMIT/CLEAN/My Projects/TemplateTest/game/data/scripts/gui</Setting> + <Group name="EngineDevelopment"> + <Setting name="toggleIntoEditor">0</Setting> + <Setting name="showEditorProfiles">0</Setting> + <Setting name="showEditorGuis">0</Setting> + </Group> + <Group name="Help"> + <Setting name="documentationURL">http://www.garagegames.com/products/torque-3d/documentation/user</Setting> + <Setting name="documentationLocal">../../../Documentation/Official Documentation.html</Setting> + <Setting name="documentationReference">../../../Documentation/Torque 3D - Script Manual.chm</Setting> + </Group> + <Group name="Rendering"> + <Setting name="drawBorderLines">1</Setting> + <Setting name="drawGuides">1</Setting> + </Group> + <Group name="Snapping"> + <Setting name="snapToGuides">1</Setting> + <Setting name="snap2GridSize">8</Setting> + <Setting name="snapToEdges">1</Setting> + <Setting name="snapToCanvas">1</Setting> + <Setting name="snapToCenters">1</Setting> + <Setting name="sensitivity">2</Setting> + <Setting name="snapToControls">1</Setting> + <Setting name="snap2Grid">0</Setting> + </Group> + <Group name="Selection"> + <Setting name="fullBox">0</Setting> + </Group> + <Group name="Library"> + <Setting name="viewType">Categorized</Setting> + </Group> + </Group> + <Group name="ShapeEditor"> + <Setting name="showBounds">0</Setting> + <Setting name="gridSize">0.1</Setting> + <Setting name="SunAngleZ">135</Setting> + <Setting name="gridDimension">40 40</Setting> + <Setting name="SunDiffuseColor">255 255 255 255</Setting> + <Setting name="SunAmbientColor">180 180 180 255</Setting> + <Setting name="AdvancedWndVisible">1</Setting> + <Setting name="showObjBox">1</Setting> + <Setting name="renderMounts">1</Setting> + <Setting name="RenderCollision">0</Setting> + <Setting name="ShowGrid">1</Setting> + <Setting name="backgroundColor">0 0 0 100</Setting> + <Setting name="highlightMaterial">1</Setting> + <Setting name="showNodes">1</Setting> + <Setting name="SunAngleX">45</Setting> + </Group> + <Group name="WorldEditor"> + <Setting name="currentEditor">WorldEditorInspectorPlugin</Setting> + <Setting name="dropType">screenCenter</Setting> + <Setting name="orthoFOV">50</Setting> + <Setting name="orthoShowGrid">1</Setting> + <Setting name="forceLoadDAE">0</Setting> + <Setting name="displayType">6</Setting> + <Setting name="undoLimit">40</Setting> + <Group name="Docs"> + <Setting name="documentationReference">../../../Documentation/Torque 3D - Script Manual.chm</Setting> + <Setting name="forumURL">http://www.garagegames.com/products/torque-3d/forums</Setting> + <Setting name="documentationURL">http://www.garagegames.com/products/torque-3d/documentation/user</Setting> + <Setting name="documentationLocal">../../../Documentation/Official Documentation.html</Setting> + </Group> + <Group name="Color"> + <Setting name="dragRectColor">255 255 0 255</Setting> + <Setting name="objectTextColor">255 255 255 255</Setting> + <Setting name="popupBackgroundColor">100 100 100 255</Setting> + <Setting name="objSelectColor">255 0 0 255</Setting> + <Setting name="objMouseOverSelectColor">0 0 255 255</Setting> + <Setting name="selectionBoxColor">255 255 0 255</Setting> + <Setting name="objMouseOverColor">0 255 0 255</Setting> + </Group> + <Group name="Tools"> + <Setting name="objectsUseBoxCenter">1</Setting> + <Setting name="snapSoft">0</Setting> + <Setting name="snapGround">0</Setting> + <Setting name="boundingBoxCollision">0</Setting> + <Setting name="snapSoftSize">2</Setting> + <Setting name="dropAtScreenCenterMax">100</Setting> + <Setting name="dropAtScreenCenterScalar">1</Setting> + </Group> + <Group name="Images"> + <Setting name="lockedHandle">tools/worldEditor/images/LockedHandle</Setting> + <Setting name="selectHandle">tools/worldEditor/images/SelectHandle</Setting> + <Setting name="defaultHandle">tools/worldEditor/images/DefaultHandle</Setting> + </Group> + <Group name="Grid"> + <Setting name="gridMinorColor">51 51 51 100</Setting> + <Setting name="gridOriginColor">255 255 255 100</Setting> + <Setting name="gridSize">1</Setting> + <Setting name="gridColor">102 102 102 100</Setting> + <Setting name="gridSnap">0</Setting> + </Group> + <Group name="ObjectIcons"> + <Setting name="fadeIcons">1</Setting> + <Setting name="fadeIconsStartAlpha">255</Setting> + <Setting name="fadeIconsEndAlpha">0</Setting> + <Setting name="fadeIconsStartDist">8</Setting> + <Setting name="fadeIconsEndDist">20</Setting> + </Group> + <Group name="Render"> + <Setting name="renderObjHandle">1</Setting> + <Setting name="renderObjText">1</Setting> + <Setting name="renderSelectionBox">1</Setting> + <Setting name="showMousePopupInfo">1</Setting> + <Setting name="renderPopupBackground">1</Setting> + </Group> + </Group> + <Group name="NavEditor"> + <Setting name="SpawnClass">AIPlayer</Setting> + <Setting name="spawnDatablock">DefaultPlayerData</Setting> + <Setting name="backgroundBuild">1</Setting> + </Group> + <Group name="RoadEditor"> + <Setting name="DefaultWidth">10</Setting> + <Setting name="HoverNodeColor">255 255 255 255</Setting> + <Setting name="HoverSplineColor">255 0 0 255</Setting> + <Setting name="materialName">DefaultDecalRoadMaterial</Setting> + <Setting name="SelectedSplineColor">0 255 0 255</Setting> + </Group> + <Group name="TerrainEditor"> + <Setting name="currentAction">raiseHeight</Setting> + <Group name="ActionValues"> + <Setting name="softSelectRadius">50</Setting> + <Setting name="SlopeMinAngle">0</Setting> + <Setting name="softSelectDefaultFilter">1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000</Setting> + <Setting name="SlopeMaxAngle">90</Setting> + <Setting name="softSelectFilter">1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000</Setting> + <Setting name="setHeightVal">100</Setting> + <Setting name="adjustHeightVal">10</Setting> + <Setting name="noiseFactor">1</Setting> + <Setting name="scaleVal">1</Setting> + <Setting name="smoothFactor">0.1</Setting> + </Group> + <Group name="Brush"> + <Setting name="brushSoftness">1</Setting> + <Setting name="brushType">ellipse</Setting> + <Setting name="maxBrushSize">40 40</Setting> + <Setting name="brushPressure">1</Setting> + <Setting name="brushSize">1 1</Setting> + </Group> + </Group> + <Group name="MeshRoadEditor"> + <Setting name="bottomMaterialName">DefaultRoadMaterialOther</Setting> + <Setting name="topMaterialName">DefaultRoadMaterialTop</Setting> + <Setting name="HoverSplineColor">255 0 0 255</Setting> + <Setting name="DefaultDepth">5</Setting> + <Setting name="SelectedSplineColor">0 255 0 255</Setting> + <Setting name="sideMaterialName">DefaultRoadMaterialOther</Setting> + <Setting name="DefaultNormal">0 0 1</Setting> + <Setting name="HoverNodeColor">255 255 255 255</Setting> + <Setting name="DefaultWidth">10</Setting> + </Group> + <Group name="RiverEditor"> + <Setting name="DefaultDepth">5</Setting> + <Setting name="HoverNodeColor">255 255 255 255</Setting> + <Setting name="DefaultWidth">10</Setting> + <Setting name="HoverSplineColor">255 0 0 255</Setting> + <Setting name="DefaultNormal">0 0 1</Setting> + <Setting name="SelectedSplineColor">0 255 0 255</Setting> + </Group> + <Group name="AxisGizmo"> + <Setting name="rotationSnap">15</Setting> + <Setting name="snapRotations">0</Setting> + <Setting name="mouseRotateScalar">0.8</Setting> + <Setting name="mouseScaleScalar">0.8</Setting> + <Setting name="renderWhenUsed">0</Setting> + <Setting name="renderInfoText">1</Setting> + <Setting name="axisGizmoMaxScreenLen">100</Setting> + <Group name="Grid"> + <Setting name="planeDim">500</Setting> + <Setting name="renderPlane">0</Setting> + <Setting name="gridSize">10 10 10</Setting> + <Setting name="gridColor">255 255 255 20</Setting> + <Setting name="renderPlaneHashes">0</Setting> + <Setting name="snapToGrid">0</Setting> + </Group> + </Group> + <Group name="DatablockEditor"> + <Setting name="libraryTab">0</Setting> + </Group> + <Group name="ConvexEditor"> + <Setting name="materialName">Grid512_OrangeLines_Mat</Setting> + </Group> + <Group name="LevelInformation"> + <Group name="levels"> + <Group name="BlankRoom.mis"> + <Setting name="cameraSpeed">25</Setting> + </Group> + <Group name="Empty_Room.mis"> + <Setting name="cameraSpeed">25</Setting> + </Group> + <Group name="Empty Terrain.mis"> + <Setting name="cameraSpeed">25</Setting> + </Group> + </Group> + </Group> +</EditorSettings> diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/Profiles.ed.cs b/Templates/BaseGame/game/tools/shapeEditor/gui/Profiles.ed.cs new file mode 100644 index 000000000..cb42ef169 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/Profiles.ed.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Shape Editor Profiles +//----------------------------------------------------------------------------- + +singleton GuiControlProfile(GuiShapeEdScrollProfile : GuiEditorScrollProfile) +{ + // Don't clear the scroll area (since we need to be able to see the GuiContainer + // underneath that provides the fill color for the header row) + opaque = false; + category = "Editor"; +}; + +singleton GuiControlProfile(GuiShapeEdTextListProfile : ToolsGuiTextListProfile) +{ + // Customise the not-active font used for the header row + fontColorNA = "75 75 75"; + category = "Editor"; +}; + +singleton GuiControlProfile(GuiShapeEdRolloutProfile : GuiInspectorRolloutProfile0) +{ + bitmap = "tools/editorClasses/gui/images/rollout"; + category = "Editor"; +}; + +singleton GuiControlProfile( GuiShapeEdTransitionSliderProfile ) +{ + bitmap = "tools/shapeEditor/images/transition_slider"; + category = "Core"; +}; diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorSettingsTab.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorSettingsTab.gui new file mode 100644 index 000000000..1a455e9a6 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorSettingsTab.gui @@ -0,0 +1,549 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ShapeEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EShapeEditorSettingsPage) { + fitBook = "1"; + text = "Shape Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Colors"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 10"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "ShapeEdShapeView.sunDiffuse = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "ShapeEditor/SunDiffuseColor"; + editorSettingsWrite = "ShapeEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sun Diffuse:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 30"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "ShapeEdShapeView.sunAmbient = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "ShapeEditor/SunAmbientColor"; + editorSettingsWrite = "ShapeEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Sun Ambient:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 50"; + Extent = "208 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + className = "ESettingsWindowColor"; + editorSettingsRead = "ShapeEdPreviewGui-->previewBackground.color = ColorIntToFloat(EditorSettings.value(%this.editorSettingsValue));"; + editorSettingsValue = "ShapeEditor/BackgroundColor"; + editorSettingsWrite = "ShapeEditorPlugin.writeSettings();"; + + new GuiTextCtrl() { + text = "Background:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "70 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "80 0"; + Extent = "104 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + className = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + className = "ESettingsWindowColorButton"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Grid"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Grid Size:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ShapeEdShapeView.gridSize = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "ShapeEditor/GridSize"; + editorSettingsWrite = "ShapeEditorPlugin.writeSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Grid Dimension:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ShapeEdShapeView.gridDimension = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "ShapeEditor/GridDimension"; + editorSettingsWrite = "ShapeEditorPlugin.writeSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorToolbar.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorToolbar.ed.gui new file mode 100644 index 000000000..0435e3837 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/ShapeEditorToolbar.ed.gui @@ -0,0 +1,295 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ShapeEditorToolbar, EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = ""; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "102 0"; + Extent = "550" SPC getWord(EditorGuiToolbar.extent, 1); + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + canMove = "0"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + EdgeSnap = "0"; + text =""; + + new GuiContainer() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "menubarProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 32"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 7"; + extent = "86 16"; + minExtent = "8 8"; + visible = "1"; + text = "Preview Settings"; + maxLength = "255"; + helpTag = "0"; + }; + new GuiBitmapCtrl() { + Profile = "ToolsGuiDefaultProfile"; + position = "94 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "showGridBtn"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "100 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.renderGrid = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Show grid"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/show-grid"; + text = ""; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "fitToShapeBtn"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "134 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.fitToShape();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Fit Camera to Shape (F)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/fit-selection"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "orbitNodeBtn"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "168 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.orbitNode = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Orbit the selected node"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/orbit-cam"; + text = ""; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapCtrl() { + Profile = "ToolsGuiDefaultProfile"; + position = "202 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "showNodes"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "210 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.renderNodes = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Show Nodes (N)"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/shownodes_btn"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + internalName = "ghostMode"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "243 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.renderGhost = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle shape transparency in the preview window (T)"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/ghost_btn"; + buttonType = "ToggleButton"; + groupNum = "0"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + internalName = "wireframeMode"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "276 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "shapeEditorWireframeMode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle shape wireframe in the preview window (R)"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/show-wireframe"; + buttonType = "ToggleButton"; + groupNum = "0"; + useMouseEvents = "0"; + }; + new GuiBitmapCtrl() { + Profile = "ToolsGuiDefaultProfile"; + position = "309 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "showBounds"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "315 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.renderbounds = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle shape bounding box in the preview window"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/object-bounds"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "showObjBox"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "348 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdShapeView.renderObjBox = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle selected object bounding box in the preview window"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/object-fit-bounds"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "renderColMeshes"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "381 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "shapeEdShapeView.renderColMeshes = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle rendering of collision meshes in the preview window"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/collision-shape"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapCtrl() { + Profile = "ToolsGuiDefaultProfile"; + position = "415 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + new GuiBitmapButtonCtrl() { + internalName = "showAdvanced"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "423 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAdvancedWindow.setVisible( $ThisControl.getValue() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Advanced Properties Window"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/detail-levels_btn"; + buttonType = "ToggleButton"; + groupNum = "0"; + useMouseEvents = "0"; + }; + }; +}; diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAdvancedWindow.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAdvancedWindow.ed.gui new file mode 100644 index 000000000..4a4793072 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAdvancedWindow.ed.gui @@ -0,0 +1,1845 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCollapseCtrl(ShapeEdAdvancedWindow, EditorGuiGroup) { + text = "Advanced Properties"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + closeCommand = "ShapeEditorToolbar-->showAdvanced.performClick();"; + EdgeSnap = "1"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = getWord($pref::Video::mode, 0) - 209 - 209 SPC getWord(EditorGuiToolbar.extent, 1) - 1; + extent = "210 272"; + MinExtent = "210 253"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Profile = "ToolsGuiWindowProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + minSize = "50 50"; + + new GuiTabBookCtrl() { + TabPosition = "Top"; + TabMargin = "6"; + MinTabWidth = "32"; + docking = "client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "4 24"; + extent = "202 243"; + MinExtent = "8 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabBookProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "tabBook"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTabPageCtrl() { + text = "Details"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Position = "0 19"; + extent = "202 224"; + MinExtent = "0 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabPageProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + docking = "client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 0"; + extent = "202 224"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + position = "0 0"; + extent = "202 157"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "inspectorStyleRolloutDarkProfile"; + + new GuiTextCtrl() { + text = "Levels"; + position = "4 1"; + Extent = "192 16"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Levels"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "5 22"; + Extent = "49 13"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiCheckBoxProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.fixedDetail"; + Command = "ShapeEdAdvancedWindow-->detailSlider.setActive($ThisControl.getValue()); ShapeEdAdvancedWindow-->levelsInactive.setVisible( !$ThisControl.getValue() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Allow the slider to select the current detail level"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0 0"; + ticks = "1"; + snap = "1"; + value = "0"; + position = "57 22"; + Extent = "118 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.currentDL"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Drag the slider to change the current detail level"; + hovertime = "1000"; + isContainer = "0"; + internalName = "detailSlider"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(){ + bitmap = "tools/gui/images/inactive-overlay"; + position = "57 19"; + Extent = "122 20"; + tooltip = "Levels needs to be selected to enable the detail level slider"; + hovertime = "500"; + isContainer = true; + internalName = "levelsInactive"; + }; + new GuiTextCtrl() { + text = "0"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "182 20"; + Extent = "15 16"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.currentDL"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Index of the current detail level"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Polys"; + position = "37 40"; + extent = "26 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "0"; + position = "67 40"; + Extent = "40 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.detailPolys"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of polygons in the current detail level"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Size"; + position = "127 40"; + extent = "24 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "160 39"; + extent = "35 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.detailSize"; + AltCommand = "ShapeEdAdvancedWindow.onEditDetailSize();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Edit this value to change the size of the current detail"; + hovertime = "1000"; + internalName = "detailSize"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Pixels"; + position = "35 60"; + extent = "28 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "0"; + position = "67 60"; + Extent = "40 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.pixelSize"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Current size (in pixels) of the shape"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Distance"; + position = "109 60"; + Extent = "42 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + position = "160 60"; + extent = "38 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.orbitDist"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Current distance from the shape to the camera"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Materials"; + position = "20 80"; + extent = "43 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + position = "67 80"; + extent = "40 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.numMaterials"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of materials used by all meshes at this detail level"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Bones"; + position = "120 80"; + extent = "31 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "5"; + position = "160 80"; + extent = "38 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.numBones"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of bones at this detail level (skins only)"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Primitives"; + position = "19 100"; + extent = "44 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = ""; + position = "67 100"; + extent = "40 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.numDrawCalls"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Total number of mesh primitives (triangle lists) at this detail level"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Weights"; + position = "109 100"; + Extent = "42 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "5"; + position = "160 100"; + extent = "38 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.numWeights"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of vertex weights at this detail level (skins only)"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Col Meshes"; + position = "7 120"; + extent = "56 16"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + new GuiTextCtrl() { + text = ""; + position = "67 120"; + extent = "40 16"; + horizSizing = "right"; + vertSizing = "bottom"; + Variable = "ShapeEdShapeView.colMeshes"; + }; + new GuiTextCtrl() { + text = "Col Polys"; + position = "108 120"; + extent = "43 16"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + new GuiTextCtrl() { + text = ""; + position = "160 120"; + extent = "38 16"; + horizSizing = "right"; + vertSizing = "bottom"; + Variable = "ShapeEdShapeView.colPolys"; + }; + }; + new GuiContainer() { + position = "0 138"; + Extent = "202 87"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "inspectorStyleRolloutDarkProfile"; + isContainer = "1"; + + new GuiTextCtrl() { // Header + text = "Imposters"; + position = "4 1"; + Extent = "192 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextProfile"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Use Imposters"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "113 2"; + Extent = "83 13"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiCheckBoxProfile"; + Visible = "1"; + Command = "ShapeEdDetails.onToggleImposter( $ThisControl.getValue() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Controls whether this shape uses an imposter detail level"; + hovertime = "1000"; + isContainer = "0"; + internalName = "bbUseImposters"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Detail Level"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "6 23"; + Extent = "57 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "68 22"; + Extent = "36 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Specifies the detail level used to generate the imposters"; + hovertime = "1000"; + internalName = "bbDetailLevel"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Dimension"; + position = "6 43"; + Extent = "57 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "68 43"; + Extent = "36 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Specifies the dimension (width/height) of the imposters in pixels"; + hovertime = "1000"; + internalName = "bbDimension"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "X Steps"; + position = "6 65"; + Extent = "57 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "68 64"; + Extent = "36 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of steps in the horizontal axis"; + hovertime = "1000"; + internalName = "bbEquatorSteps"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Include Poles"; + groupNum = "-1"; + buttonType = "ToggleButton"; + position = "113 24"; + Extent = "83 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiCheckBoxProfile"; + Visible = "1"; + Command = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Specifies whether to include the poles (top and bottom) of the shape"; + hovertime = "1000"; + internalName = "bbIncludePoles"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Y Steps"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "116 44"; + Extent = "41 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + Tooltip = "Number of steps in the vertical axis"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "161 43"; + Extent = "36 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "bbPolarSteps"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Y Angle"; + position = "116 65"; + Extent = "41 16"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextRightProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Polar Angle - Y axis"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "161 64"; + Extent = "36 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditImposter();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "bbPolarAngle"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl(){ + bitmap = "tools/gui/images/inactive-overlay"; + position = "4 18"; + Extent = "193 64"; + tooltip = "Imposters must be enabled, and an imposter detail level selected to edit these properties"; + hovertime = "500"; + isContainer = "1"; + internalName = "imposterInactive"; + }; + }; + }; + }; + new GuiTabPageCtrl() { + text = "Mounting"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Position = "0 19"; + extent = "202 224"; + MinExtent = "0 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabPageProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + isContainer = "1"; + + new GuiControl(){ + docking = "client"; + Margin = "0 0 0 0"; + Profile = "ToolsGuiScrollProfile"; + position = "0 0"; + extent = "202 224"; + + }; + new GuiContainer(ShapeEdMountWindow) { + docking = "none"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + position = "0 0"; + extent = "202 224"; + MinExtent = "8 8"; + HorizSizing = "width"; + vertSizing = "height"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Render mounted shapes"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "2 2"; + extent = "139 13"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiCheckBoxProfile"; + Visible = "1"; + Variable = "ShapeEdShapeView.renderMounts"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Controls whether mounted shapes will be rendered in the 3D view"; + hovertime = "1000"; + isContainer = "0"; + internalName = "renderMounts"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 17"; + extent = "202 124"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "GuiShapeEdScrollProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + canSave = "1"; + isContainer = "1"; + + new GuiContainer() { + position = "0 0"; + extent = "200 121"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "inspectorStyleRolloutListProfile"; + }; + new GuiTextListCtrl() { + columns = "-1 0 110 152"; + fitParentWidth = "1"; + clipColumnText = "1"; + Position = "1 1"; + Extent = "200 11"; + MinExtent = "8 11"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "GuiShapeEdTextListProfile"; + Visible = "1"; + Command = "ShapeEdMountWindow.update_onMountSelectionChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "mountList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiContainer() { + position = "0 140"; + extent = "202 85"; + HorizSizing = "width"; + VertSizing = "top"; + Profile = "inspectorStyleRolloutDarkProfile"; + + new GuiTextCtrl() { + text = "Mount Item"; + position = "5 1"; + extent = "134 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "182 1"; + Extent = "16 16"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + Command = "ShapeEdMountWindow.unmountShape();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete the selected mount item"; + canSaveDynamicFields = "0"; + canSave = "1"; + isContainer = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/new"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "168 1"; + Extent = "16 16"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + Command = "ShapeEdMountWindow.mountShape(-1);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Mounts a new shape to the current model"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + /*new GuiButtonCtrl() { + text = "Unmount All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "109 97"; + extent = "78 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "top"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdMountWindow.unmountAll();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Unmount all shapes"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + };*/ + new GuiTextCtrl() { + text = "Shape"; + position = "5 21"; + extent = "33 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + }; + new GuiPopUpMenuCtrl(ShapeEdMountShapeMenu) { + position = "42 20"; + extent = "156 18"; + HorizSizing = "width"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + ToolTip = "Select the model to mount"; + }; + new GuiTextCtrl() { + text = "Node"; + position = "5 42"; + extent = "33 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + }; + new GuiPopUpMenuCtrl() { + position = "42 41"; + extent = "62 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdMountWindow.updateSelectedMount();"; + ToolTip = "Select the node on which to mount the model"; + internalName = "mountNode"; + }; + new GuiTextCtrl() { + text = "Type"; + position = "110 42"; + extent = "24 16"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiPopUpMenuCtrl() { + position = "138 41"; + extent = "60 18"; + horizSizing = "left"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdMountWindow.updateSelectedMount();"; + ToolTip = "Select the type of mounting to use"; + internalName = "mountType"; + }; + new GuiPopUpMenuCtrl() { + position = "5 62"; + extent = "99 18"; + HorizSizing = "width"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdMountWindow.setMountThreadSequence();"; + ToolTip = "Select the sequence to play on the mounted model"; + internalName = "mountSeq"; + }; + new GuiSliderCtrl(ShapeEdMountSeqSlider) { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "0"; + position = "109 64"; + extent = "68 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "top"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Drag the slider to scrub through the sequence keyframes"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/shapeEditor/images/playfwd_btn"; + groupNum = "0"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "180 62"; + Extent = "18 18"; + MinExtent = "8 2"; + HorizSizing = "left"; + vertSizing = "top"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdMountWindow.toggleMountThreadPlayback();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play forwards"; + hovertime = "1000"; + isContainer = "0"; + internalName = "mountPlayBtn"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiTabPageCtrl() { + text = "Threads"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Position = "0 19"; + extent = "202 224"; + MinExtent = "0 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabPageProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer(ShapeEdThreadWindow) { + docking = "client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 0"; + extent = "202 224"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + position = "0 0"; + extent = "203 141"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "inspectorStyleRolloutDarkProfile"; + + new GuiTextCtrl() { + text = "Thread"; + position = "4 1"; + extent = "41 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 17"; + extent = "47 124"; + MinExtent = "8 8"; + HorizSizing = "right"; + VertSizing = "height"; + Profile = "GuiShapeEdScrollProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl(ShapeEdThreadList) { + fitParentWidth = "1"; + clipColumnText = "1"; + position = "1 1"; + extent = "45 11"; + MinExtent = "8 11"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "GuiShapeEdTextListProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiTextCtrl() { + text = "Sequence"; + position = "52 1"; + extent = "53 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "46 17"; + extent = "157 124"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "GuiShapeEdScrollProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl() { + fitParentWidth = "1"; + clipColumnText = "1"; + Position = "1 1"; + extent = "155 11"; + MinExtent = "8 11"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "GuiShapeEdTextListProfile"; + Visible = "1"; + Command = "ShapeEdSequenceList.setSelectedById( $ThisControl.getSelectedId() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "seqList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "184 1"; + Extent = "16 16"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + Command = "ShapeEdThreadWindow.onRemoveThread();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete the selected thread"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/new"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "171 1"; + Extent = "16 16"; + MinExtent = "8 2"; + HorizSizing = "left"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + Command = "ShapeEdThreadWindow.onAddThread();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add a new thread"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiSliderCtrl(ShapeEdThreadSlider) { + range = "0 0"; + ticks = "0"; + snap = "0"; + value = "0"; + position = "29 146"; + extent = "133 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "top"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Drag the slider to scrub through the sequence keyframes"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/shapeEditor/images/playbkwd_btn"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "6 144"; + extent = "18 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "top"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdAnimWindow-->playBkwdBtn.performClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play backwards"; + hovertime = "1000"; + isContainer = "0"; + internalName = "playBkwdBtn"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/shapeEditor/images/pause_btn"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "166 144"; + Extent = "18 18"; + MinExtent = "8 2"; + HorizSizing = "left"; + vertSizing = "top"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdAnimWindow-->pauseBtn.performClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Pause (SPACE)"; + hovertime = "1000"; + isContainer = "0"; + internalName = "pauseBtn"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/shapeEditor/images/playfwd_btn"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + position = "184 144"; + Extent = "18 18"; + MinExtent = "8 2"; + HorizSizing = "left"; + vertSizing = "top"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdAnimWindow-->playFwdBtn.performClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play forwards"; + hovertime = "1000"; + isContainer = "0"; + internalName = "playFwdBtn"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = " Transition lasts"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "3 167"; + extent = "88 13"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "top"; + Profile = "ToolsGuiCheckBoxProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Controls whether the thread will smoothly transition when a new sequence is selected"; + hovertime = "1000"; + isContainer = "0"; + internalName = "useTransitions"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "98 164"; + extent = "49 18"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "top"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Number of seconds over which to transition to the new sequence"; + hovertime = "1000"; + internalName = "transitionTime"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "seconds"; + position = "153 165"; + extent = "44 16"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "top"; + profile = "ToolsGuiTextProfile"; + }; + new GuiTextCtrl() { + text = "Transition to"; + position = "4 186"; + extent = "62 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "top"; + profile = "ToolsGuiTextProfile"; + }; + new GuiPopUpMenuCtrl() { + position = "68 185"; + extent = "133 18"; + HorizSizing = "width"; + vertSizing = "top"; + Profile = "ToolsGuiPopUpMenuProfile"; + ToolTip = "Select the start position of the new sequence"; + internalName = "transitionTo"; + }; + new GuiTextCtrl() { + text = "Target anim"; + position = "4 207"; + extent = "58 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "top"; + profile = "ToolsGuiTextProfile"; + }; + new GuiPopUpMenuCtrl() { + position = "68 206"; + extent = "133 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "top"; + Profile = "ToolsGuiPopUpMenuProfile"; + ToolTip = "Select the initial play state of the new sequence"; + internalName = "transitionTarget"; + }; + }; + }; + new GuiTabPageCtrl() { + text = "Collision"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Position = "0 19"; + extent = "202 224"; + MinExtent = "0 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabPageProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer(ShapeEdColWindow) { + docking = "client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 0"; + extent = "202 225"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Fit Type"; + position = "5 5"; + extent = "41 16"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + new GuiPopUpMenuCtrl() { + position = "70 4"; + extent = "108 18"; + horizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdColWindow.editCollision();"; + ToolTip = "Select the method used to auto-generate the collision geometry"; + internalName = "colType"; + }; + new GuiTextCtrl() { + text = "Fit Target"; + position = "5 25"; + extent = "45 16"; + horizSizing = "right"; + vertSizing = "bottom"; + }; + new GuiPopUpMenuCtrl() { + position = "70 24"; + extent = "108 18"; + horizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdColWindow.editCollision();"; + ToolTip = "Select the object to fit collision geometry to"; + internalName = "colTarget"; + }; + new GuiTextCtrl() { + text = "Max Depth"; + position = "5 47"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 8"; + ticks = "4"; + snap = "0"; + value = "4"; + position = "70 48"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullDepthText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Maximum hull split depth"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullDepth"; + }; + new GuiTextCtrl() { + text = "4"; + position = "181 47"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullDepthText"; + }; + new GuiTextCtrl() { + text = "Merge %"; + position = "5 68"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 60"; + ticks = "4"; + snap = "0"; + value = "30"; + position = "70 69"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullMergeText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Hull volume merge threshold"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullMergeThreshold"; + }; + new GuiTextCtrl() { + text = "30"; + position = "179 68"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullMergeText"; + }; + new GuiTextCtrl() { + text = "Concavity %"; + position = "5 89"; + extent = "59 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 60"; + ticks = "4"; + snap = "0"; + value = "30"; + position = "70 90"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullConcaveText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Hull concavity threshold"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullConcaveThreshold"; + }; + new GuiTextCtrl() { + text = "30"; + position = "179 89"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullConcaveText"; + }; + new GuiTextCtrl() { + text = "Max Verts"; + position = "5 110"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "8 64"; + ticks = "4"; + snap = "0"; + value = "32"; + position = "70 111"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullMaxVertsText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Maximum number of verts in a convex hull"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullMaxVerts"; + }; + new GuiTextCtrl() { + text = "32"; + position = "179 110"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullMaxVertsText"; + }; + new GuiTextCtrl() { + text = "Box %"; + position = "5 131"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 100"; + ticks = "4"; + snap = "0"; + value = "30"; + position = "70 132"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullMaxBoxErrorText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Maximum box volume error %"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullMaxBoxError"; + }; + new GuiTextCtrl() { + text = "30"; + position = "179 131"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullMaxBoxErrorText"; + }; + new GuiTextCtrl() { + text = "Sphere %"; + position = "5 152"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 100"; + ticks = "4"; + snap = "0"; + value = "30"; + position = "70 153"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullMaxSphereErrorText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Maximum sphere volume error %"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullMaxSphereError"; + }; + new GuiTextCtrl() { + text = "30"; + position = "179 152"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullMaxSphereErrorText"; + }; + new GuiTextCtrl() { + text = "Capsule %"; + position = "5 173"; + extent = "53 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + }; + new GuiSliderCtrl() { + range = "0 100"; + ticks = "4"; + snap = "0"; + value = "30"; + position = "70 174"; + extent = "104 14"; + MinExtent = "8 2"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "ToolsGuiSliderProfile"; + Visible = "1"; + AltCommand = "ShapeEdColWindow-->hullMaxCapsuleErrorText.setText( mFloor($ThisControl.getValue()) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Maximum capsule volume error %"; + hovertime = "1000"; + isContainer = "0"; + internalName = "hullMaxCapsuleError"; + }; + new GuiTextCtrl() { + text = "30"; + position = "179 173"; + extent = "18 16"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + internalName = "hullMaxCapsuleErrorText"; + }; + new GuiButtonCtrl() { + text = "Update Hulls"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "7 200"; + extent = "88 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdColWindow.editCollision();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Update the convex hull(s)"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Revert Changes"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "105 200"; + extent = "88 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdColWindow.update_onCollisionChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Revert changes to settings"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/inactive-overlay"; + position = "0 47"; + extent = "199 175"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + internalName = "hullInactive"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +new GuiControl(ShapeEdWaitGui,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiControl() { + isContainer = "1"; + Profile = "editorMenu_wBorderProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "277 271"; + Extent = "245 57"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Dialog"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = ""; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextBoldCenterProfile"; + HorizSizing = "width"; + VertSizing = "center"; + position = "5 19"; + Extent = "236 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + internalName = "message"; + }; + }; +}; + +function ShapeEdWaitGui::show(%this, %text) +{ + %this-->message.setText( %text ); + Canvas.pushDialog( %this ); + Canvas.repaint(); +} + +function ShapeEdWaitGui::hide(%this) +{ + Canvas.popDialog( %this ); +} + +function ShapeEdWaitGui::onWake(%this) +{ + %res = %this.getExtent(); + %resX = getWord( %res, 0 ); + %resY = getWord( %res, 1 ); + + %dialog = %this-->Dialog; + %dialogExtent = %dialog.getExtent(); + %dialogWidth = getWord( %dialogExtent, 0 ); + %dialogHeight = getWord( %dialogExtent, 1 ); + %dialogPostion = %dialog.getPosition(); + + %posX = ( %resX / 2 ) - ( %dialogWidth / 2 ); + %posY = ( %resY / 2 ) - ( %dialogHeight / 2 ); + %dialog.setPosition( %posX, %posY ); +} diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAnimWindow.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAnimWindow.ed.gui new file mode 100644 index 000000000..79c298a27 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdAnimWindow.ed.gui @@ -0,0 +1,438 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCtrl(ShapeEdAnimWindow) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiToolbarWindowProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = -1 SPC getWord(ShapeEdPreviewGui.extent,0)-94; + Extent = "817 53"; + MinExtent = "475 53"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "0"; + canCollapse = "0"; + text = ""; + + new GuiContainer() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "5 10"; + Extent = "809 "; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + // Sequence playback controls + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 3"; + Extent = "809 38"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiTextCtrl() { + HorizSizing = "left"; + VertSizing = "top"; + position = "740 19"; + Extent = "35 16"; + text = "Frame:"; + }; + new GuiTextCtrl() { + HorizSizing = "left"; + VertSizing = "top"; + Profile = "ToolsGuiTextProfile"; + position = "778 19"; + Extent = "26 18"; + Variable = "$ShapeEdCurrentFrame"; + }; + + new GuiTextEditCtrl() { + internalName = "seqIn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "28 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdSequences.onEditSeqInOut(\"in\", $ThisControl.getText());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Set the In Point to the Current Frame"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "5"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + + new GuiSliderCtrl(ShapeEdSeqSlider) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "35 4"; + Extent = "736 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + range = "0 255"; + ticks = "0"; + value = "0"; + Variable = "$ShapeEdCurrentFrame"; + }; + + new GuiTextEditCtrl() { + internalName = "seqOut"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "778 0"; + Extent = "28 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdSequences.onEditSeqInOut(\"out\", $ThisControl.getText());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Set the Out Point to the Current Frame"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "5"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + + // VCR style buttons: back step_back play step_fwd fwd + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "center"; + VertSizing = "top"; + position = "194 17"; + extent = "420 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "28 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdSequences.onEditSeqInOut(\"in\", ShapeEdSeqSlider.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Set the in position to the current frame (I)"; + hovertime = "1000"; + text = "in"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "48 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setKeyframe( ShapeEdAnimWindow-->seqIn.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Skip to in frame (SHIFT -)"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/back_btn"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "76 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setKeyframe( mCeil(ShapeEdSeqSlider.getValue() - 1) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Previous frame (-)"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/stepback_btn"; + internalName = "stepBkwdBtn"; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "114 0"; + Extent = "94 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl() { + internalName = "playBkwdBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setNoProxySequence(); ShapeEdAnimWindow.setThreadDirection( -1 );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play sequence in reverse"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/playbkwd_btn"; + }; + new GuiBitmapButtonCtrl() { + internalName = "pauseBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "38 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setThreadDirection( 0 );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle pause (SPACE)"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/pause_btn"; + }; + new GuiBitmapButtonCtrl() { + internalName = "playFwdBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "76 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setNoProxySequence(); ShapeEdAnimWindow.setThreadDirection( 1 );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play sequence"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/playfwd_btn"; + }; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "228 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setKeyframe( mFloor(ShapeEdSeqSlider.getValue() + 1) );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Next frame (+)"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/stepfwd_btn"; + internalName = "stepFwdBtn"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "266 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.setKeyframe( ShapeEdAnimWindow-->seqOut.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Skip to out frame (SHIFT +)"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/fwd_btn"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + VertSizing = "bottom"; + position = "306 0"; + Extent = "28 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdSequences.onEditSeqInOut(\"out\", ShapeEdSeqSlider.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Set the out position to the current frame (O)"; + hovertime = "1000"; + text = "out"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + internalName = "pingpong"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + horizSizing = "left"; + VertSizing = "bottom"; + position = "365 0"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdAnimWindow.togglePingPong();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle 'pingpong' mode on the current thread"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + bitmap = "tools/shapeEditor/images/pingpong_btn"; + }; + new GuiTextEditCtrl() { + internalName = "timeScale"; + Profile = "ToolsGuiTextEditProfile"; + horizSizing = "left"; + VertSizing = "bottom"; + position = "390 0"; + extent = "30 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Text = "1.0"; + AltCommand = "ShapeEdShapeView.setTimeScale( $ThisControl.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Edit this value to change the playback speed for all threads"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; + new GuiBitmapButtonCtrl() { + internalName = "seqInBar"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "39 0"; + Extent = "8 13"; + MinExtent = "1 1"; + bitmap = "tools/shapeEditor/images/seq_bar-in"; + ToolTip = "Set the In Point to the Current Frame"; + Command = "ShapeEdSequences.onEditSeqInOut(\"in\", ShapeEdSeqSlider.getValue());"; + }; + new GuiBitmapButtonCtrl() { + internalName = "seqOutBar"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "765 0"; + Extent = "8 13"; + MinExtent = "1 1"; + bitmap = "tools/shapeEditor/images/seq_bar-out"; + ToolTip = "Set the Out Point to the Current Frame"; + Command = "ShapeEdSequences.onEditSeqInOut(\"out\", ShapeEdSeqSlider.getValue());"; + }; + }; +}; + +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPreviewWindow.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPreviewWindow.ed.gui new file mode 100644 index 000000000..81b815d5c --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPreviewWindow.ed.gui @@ -0,0 +1,85 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(ShapeEdPreviewGui) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0" SPC (getWord(EditorGuiToolbar.extent, 1)-1); + Docking = "Client"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiSwatchButtonCtrl() { + internalName = "previewBackground"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiInspectorSwatchButtonProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "-210 -40"; + Extent = getWord(ShapeEdPreviewGui.extent,0)+212 + SPC getWord(ShapeEdPreviewGui.extent,0)+42; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + color = "0 0 0 .39"; + }; + new GuiShapeEdPreview(ShapeEdShapeView) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "-209 -90"; + Extent = getWord(ShapeEdPreviewGui.extent,0)+209 + SPC getWord(ShapeEdPreviewGui.extent, 1)+90; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + renderMissionArea = "0"; + GizmoProfile = "GlobalGizmoProfile"; + cameraZRot = "0"; + forceFOV = "0"; + gridColor = "0 0 0 140"; + renderNodes = "0"; + renderObjBox = "0"; + renderMounts = "0"; + renderColMeshes = "0"; + selectedNode = "-1"; + sunDiffuse = "255 255 255 255"; + sunAmbient = "180 180 180 255"; + timeScale = "1.0"; + fixedDetail = "0"; + orbitNode = "0"; + }; + }; +}; + +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPropWindow.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPropWindow.ed.gui new file mode 100644 index 000000000..e061679c2 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdPropWindow.ed.gui @@ -0,0 +1,1474 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiWindowCollapseCtrl(ShapeEdPropWindow) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) + getWord(ShapeEdSelectWindow.extent, 1) - 2; + Extent = "210 484"; + MinExtent = "210 352"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Properties"; + + //--------------------------------------------------------------------- + // Sequence and Node editors + new GuiTabBookCtrl(ShapeEdSeqNodeTabBook) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "4 42"; + Extent = "202 437"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "6"; + MinTabWidth = "32"; + + //--------------------------------------------------------------- + // Sequence editor + new GuiTabPageCtrl(ShapeEdSequences) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 19"; + Extent = "202 418"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Seq"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + Profile = "GuiInspectorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 418"; + MinExtent = "0 8"; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 418"; + MinExtent = "0 8"; + }; + + // Sequence list + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 211"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiShapeEdScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 211"; + MinExtent = "8 25"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiContainer() { + internalName = "sequenceListHeader"; + Profile = "inspectorStyleRolloutListProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "200 19"; + MinExtent = "8 2"; + }; + new GuiTextListCtrl(ShapeEdSequenceList) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiShapeEdTextListProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 20"; + MinExtent = "8 11"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdPropWindow.update_onSeqSelectionChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + enumerate = "0"; + resizeCell = "1"; + columns = "0 100 145 190 235"; + fitParentWidth = "0"; + clipColumnText = "1"; + }; + }; + }; + + // Sequence properties + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 210"; + Extent = "202 209"; + MinExtent = "8 11"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ // Sequence Properties Container + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "202 103"; + isContainer = true; + + new GuiTextCtrl() { // Header + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "104 16"; + text = "Sequence Properties"; + }; + new GuiTextCtrl() { // Name + HorizSizing = "right"; + VertSizing = "bottom"; + position = "16 22"; + Extent = "27 16"; + text = "Name"; + }; + new GuiTextEditCtrl() { + internalName = "seqName"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "46 21"; + Extent = "152 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdSequences.onEditName();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Name of the selected sequence (edit to rename)"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "256"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + // animation dropdown + new GuiTextCtrl() { + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "-5 42"; + Extent = "48 18"; + text = "Source"; + tooltip = "Animation source data"; + }; + new GuiPopUpMenuCtrl(ShapeEdSeqFromMenu) { + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "46 41"; + Extent = "91 18"; + }; + // Start Frame + new GuiTextCtrl() { + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "32 62"; + Extent = "11 16"; + text = "in"; + }; + new GuiTextEditCtrl() { + internalName = "startFrame"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "46 61"; + Extent = "32 18"; + text = ""; + AltCommand = "ShapeEdSequences.onEditSeqInOut(\"in\", $ThisControl.getText());"; + }; + // End frame + new GuiTextCtrl() { + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "85 62"; + Extent = "18 16"; + text = "out"; + }; + new GuiTextEditCtrl() { + internalName = "endFrame"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "105 61"; + Extent = "32 18"; + text = ""; + AltCommand = "ShapeEdSequences.onEditSeqInOut(\"out\", $ThisControl.getText());"; + }; + // Cyclic flag + new GuiCheckBoxCtrl() { + internalName = "cyclicFlag"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "143 43"; + Extent = "39 13"; + Command = "ShapeEdSequences.onToggleCyclic();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Loop Animation. Toggles the cyclic flag."; + hovertime = "1000"; + text = "Loop"; + }; + // Priority + new GuiTextCtrl() { + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "136 62"; + Extent = "41 16"; + text = "Priority"; + }; + new GuiTextEditCtrl() { + internalName = "priority"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "179 61"; + Extent = "19 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdSequences.onEditPriority();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Priority of the selected sequence"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "5"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + // Blend animation dropdown + new GuiCheckBoxCtrl() { + internalName = "blendFlag"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "2 82"; + Extent = "45 16"; + Command = "ShapeEdSequences.onEditBlend();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle the blend flag for the selected sequence"; + hovertime = "1000"; + text = "Blend"; + }; + new GuiPopUpMenuCtrl() { + internalName = "blendSeq"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "46 81"; + Extent = "91 18"; + tooltip = "Blend reference sequence"; + Command = "ShapeEdSequences.onEditBlend();"; + }; + + // Blend frame + new GuiTextCtrl() { + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "142 81"; + Extent = "29 18"; + text = "Frame"; + }; + new GuiTextEditCtrl() { + internalName = "blendFrame"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "173 81"; + Extent = "25 18"; + text = ""; + tooltip = "Blend reference frame"; + AltCommand = "ShapeEdSequences.onEditBlend();"; + }; + }; + new GuiContainer(){ // Triggers Container + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 102"; + Extent = "202 106"; + isContainer = true; + + // Triggers + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 0"; + Extent = "50 18"; + text = "Triggers"; + }; + new GuiBitmapButtonCtrl() { + internalName = "addTriggerBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "170 2"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdSequences.onAddTrigger();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add a new trigger"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + }; + new GuiBitmapButtonCtrl() { + internalName = "deleteTriggerBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "185 2"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdTriggerList.onDeleteSelection();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete the selected trigger"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + // Trigger list + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 17"; + Extent = "202 66"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiShapeEdScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 66"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiContainer() { + Profile = "inspectorStyleRolloutListProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "200 19"; + }; + new GuiTextListCtrl(ShapeEdTriggerList) { + canSaveDynamicFields = "0"; + Profile = "GuiShapeEdTextListProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "177 16"; + MinExtent = "8 11"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdPropWindow.onTriggerSelectionChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + enumerate = "0"; + resizeCell = "1"; + columns = "-1 0 60 118"; + fitParentWidth = "1"; + clipColumnText = "1"; + }; + }; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 85"; + Extent = "35 18"; + text = "Frame"; + }; + new GuiTextEditCtrl() { + internalName = "triggerFrame"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "36 85"; + Extent = "32 18"; + text = ""; + AltCommand = "ShapeEdTriggerList.onEditSelection();"; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "75 85"; + Extent = "35 18"; + text = "Trigger"; + }; + new GuiTextEditCtrl() { + internalName = "triggerNum"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "110 85"; + Extent = "32 18"; + text = ""; + AltCommand = "ShapeEdTriggerList.onEditSelection();"; + }; + new GuiCheckBoxCtrl() { + internalName = "triggerOnOff"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "152 87"; + Extent = "47 13"; + text = "On/off"; + Command = "ShapeEdTriggerList.onEditSelection();"; + }; + }; + }; + }; + + //--------------------------------------------------------------- + // Node Editor + new GuiTabPageCtrl(ShapeEdNodes) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 19"; + Extent = "202 418"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Node"; + maxLength = "1024"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 418"; + MinExtent = "0 8"; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 288"; + MinExtent = "8 0"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(ShapeEdNodeTreeView) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "height"; + Position = "1 1"; + Extent = "122 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + internalNamesOnly = "0"; + }; + }; + new GuiContainer(){ // Node Properties Container + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "top"; + position = "0 287"; + Extent = "202 131"; + isContainer = true; + + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "104 16"; + text = "Node Properties"; + }; + // Node property labels + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "6 18"; + Extent = "50 108"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + position = "9 6"; + Extent = "40 16"; + text = "Name"; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + position = "10 26"; + Extent = "40 16"; + text = "Parent"; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + position = "-5 49"; + Extent = "54 16"; + text = "Transform"; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + position = "11 73"; + Extent = "39 16"; + text = "Position"; + }; + new GuiTextCtrl() { + HorizSizing = "right"; + VertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + position = "8 93"; + Extent = "42 16"; + text = "Rotation"; + }; + }; + + // Node properties + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "49 16"; + Extent = "155 109"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiTextEditCtrl() { + internalName = "nodeName"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "12 5"; + Extent = "137 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdNodes.onEditName();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Name of the selected node (edit to rename)"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiPopUpMenuCtrl(ShapeEdNodeParentMenu) { + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "12 25"; + Extent = "137 18"; + tooltip = "Selected node's parent"; + }; + new GuiIconButtonCtrl() { + internalName = "worldTransform"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "12 45"; + Extent = "65 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdPropWindow.update_onNodeTransformChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "View global node transform"; + hovertime = "1000"; + text = "World"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + buttonMargin = "0 4"; + iconBitmap = "tools/gui/images/menubar/world-transform_n"; + textMargin = "25"; + }; + new GuiIconButtonCtrl() { + internalName = "objectTransform"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "84 45"; + Extent = "65 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdPropWindow.update_onNodeTransformChanged();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "View local node transform (relative to parent)"; + hovertime = "1000"; + text = "Object"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + buttonMargin = "0 4"; + iconBitmap = "tools/gui/images/menubar/object-transform_n"; + textMargin = "26"; + }; + new GuiTextEditCtrl() { + internalName = "nodePosition"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "12 72"; + Extent = "137 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdNodes.onEditTransform();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Node position (x y z)"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextEditCtrl() { + internalName = "nodeRotation"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "12 92"; + Extent = "137 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "ShapeEdNodes.onEditTransform();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Node rotation (axis.x axis.y axis.z angle)"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + }; + }; + }; + + //--------------------------------------------------------------- + // Details/Objects + new GuiTabPageCtrl(ShapeEdDetails) { + fitBook = "0"; + text = "Detail"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + position = "0 19"; + extent = "202 418"; + MinExtent = "0 -500"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabPageProfile"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + + new GuiContainer() { + position = "0 0"; + extent = "202 418"; + minExtent = "0 8"; + horizSizing = "width"; + vertSizing = "height"; + profile = "GuiInspectorProfile"; + isContainer = "1"; + }; + new GuiBitmapBorderCtrl() { + position = "0 0"; + extent = "202 418"; + minExtent = "0 8"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiTabBorderProfile"; + isContainer = "1"; + }; + + // Detail/object tree + new GuiControl() { + Position = "0 0"; + extent = "202 276"; + MinExtent = "8 8"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + Position = "0 0"; + extent = "202 276"; + MinExtent = "8 25"; + HorizSizing = "width"; + VertSizing = "height"; + Profile = "GuiShapeEdScrollProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTreeViewCtrl(ShapeEdDetailTree) { + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "0"; + showRoot = "0"; + Position = "1 1"; + extent = "85 110"; + MinExtent = "8 8"; + HorizSizing = "right"; + VertSizing = "height"; + Profile = "ToolsGuiTreeViewProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + + // Detail/Object properties + new GuiControl() { + position = "0 275"; + extent = "202 143"; + MinExtent = "8 8"; + HorizSizing = "width"; + vertSizing = "top"; + Profile = "ToolsGuiDefaultProfile"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiContainer() { + position = "0 0"; + extent = "202 143"; + HorizSizing = "width"; + VertSizing = "bottom"; + Profile = "inspectorStyleRolloutDarkProfile"; + isContainer = "1"; + + new GuiTextCtrl() { // Header + text = "Detail/Object Properties"; + position = "4 1"; + extent = "112 16"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiTextEditCtrl() { + position = "5 23"; + extent = "130 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditName();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Edit this value to rename the current object or detail"; + hovertime = "1000"; + internalName = "meshName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + position = "157 23"; + extent = "40 18"; + MinExtent = "8 2"; + HorizSizing = "right"; + VertSizing = "bottom"; + Profile = "ToolsGuiTextEditProfile"; + Visible = "1"; + AltCommand = "ShapeEdDetails.onEditSize();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Edit this value to change the size of the current mesh or detail"; + hovertime = "1000"; + internalName = "meshSize"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Billboarding"; + position = "5 44"; + extent = "57 16"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiPopUpMenuCtrl() { + position = "88 45"; + extent = "109 18"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdDetails.onEditBBType();"; + ToolTip = "The type of billboarding used by the mesh"; + internalName = "bbType"; + }; + new GuiTextCtrl() { + text = "Object Node"; + position = "5 66"; + extent = "60 16"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiPopUpMenuCtrl() { + position = "88 67"; + extent = "109 18"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + Command = "ShapeEdDetails.onSetObjectNode();"; + ToolTip = "The node this object is attached to (all detail levels)"; + internalName = "objectNode"; + }; + new GuiBitmapCtrl(){ + bitmap = "tools/gui/images/inactive-overlay"; + position = "4 45"; + extent = "193 42"; + tooltip = "A mesh must be selected to edit these properties"; + hovertime = "500"; + isContainer = "1"; + internalName = "editMeshInactive"; + }; + new GuiButtonCtrl() { + text = "Import Shape into..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "4 91"; + extent = "102 22"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEdDetails.onAddMeshFromFile(\"\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add geometry from a different model file into the current shape"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + position = "111 92"; + extent = "85 18"; + horizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiPopUpMenuProfile"; + tooltip = "Select which detail level new geometry will be added to"; + internalName = "addGeomTo"; + }; + new GuiButtonCtrl() { + text = "Re-compute bounds"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "40 117"; + extent = "122 22"; + MinExtent = "8 2"; + HorizSizing = "right"; + vertSizing = "bottom"; + Profile = "ToolsGuiButtonProfile"; + Visible = "1"; + Command = "ShapeEditor.doSetBounds();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Recompute the shape bounding box using the geometry in the current detail level"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + + //--------------------------------------------------------------- + // Materials + new GuiTabPageCtrl(ShapeEdMaterials) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 19"; + Extent = "202 418"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Mat"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + Profile = "GuiInspectorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 418"; + MinExtent = "0 8"; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 418"; + MinExtent = "0 8"; + }; + + // Material list + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 345"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiShapeEdScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 345"; + MinExtent = "8 25"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiContainer() { + internalName = "materialListHeader"; + Profile = "inspectorStyleRolloutListProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "200 19"; + MinExtent = "8 2"; + }; + new GuiTextListCtrl(ShapeEdMaterialList) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiShapeEdTextListProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 20"; + MinExtent = "8 11"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdMaterials.updateSelectedMaterial(ShapeEdMaterials-->highlightMaterial.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + enumerate = "0"; + resizeCell = "1"; + columns = "0 130"; + fitParentWidth = "0"; + clipColumnText = "1"; + }; + }; + }; + + // Material properties + new GuiContainer() { + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 344"; + Extent = "202 74"; + isContainer = true; + + new GuiTextCtrl() { // Header + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "104 16"; + text = "Material Options"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 21"; + Extent = "150 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdMaterials.editSelectedMaterial();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Open the Material Editor to edit the selected material"; + hovertime = "1000"; + text = "Edit the selected Material"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + buttonMargin = "0 4"; + iconBitmap = "tools/worldEditor/images/toolbar/matterial-editor_n"; + textMargin = "25"; + }; + new GuiCheckBoxCtrl() { + internalName = "highlightMaterial"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "7 52"; + Extent = "150 13"; + Command = "ShapeEdMaterials.updateSelectedMaterial($ThisControl.getValue());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Highlight the primitives that use the selected Material"; + hovertime = "1000"; + text = "Highlight selected Material"; + }; + }; + }; + }; + + // Save/New/Delete buttons (node or sequence, depending on which tab is active) + new GuiBitmapButtonCtrl() { + internalName = "saveBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "154 25"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEditor.saveChanges();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Save the current shape"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/save-icon"; + }; + new GuiBitmapButtonCtrl() { + internalName = "newBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "176 25"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = ""; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + }; + new GuiBitmapButtonCtrl() { + internalName = "deleteBtn"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "190 25"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = ""; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; +}; +//--- OBJECT WRITE END --- + +new GuiControl(GenImposterGui,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiControl() { + isContainer = "1"; + Profile = "editorMenu_wBorderProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "277 271"; + Extent = "245 57"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "Dialog"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Generating imposter bitmaps..."; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextBoldCenterProfile"; + HorizSizing = "width"; + VertSizing = "center"; + position = "5 19"; + Extent = "236 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; + +function GenImposterGui::onWake(%this) +{ + %res = %this.getExtent(); + %resX = getWord(%res, 0); + %resY = getWord(%res, 1); + + %dialog = %this-->Dialog; + %dialogExtent = %dialog.getExtent(); + %dialogWidth = getWord(%dialogExtent, 0); + %dialogHeight = getWord(%dialogExtent, 1); + %dialogPostion = %dialog.getPosition(); + + %posX = (%resX / 2) - (%dialogWidth / 2); + %posY = (%resY / 2) - (%dialogHeight / 2); + %dialog.setPosition(%posX, %posY); +} diff --git a/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui new file mode 100644 index 000000000..b73fe709e --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/gui/shapeEdSelectWindow.ed.gui @@ -0,0 +1,504 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + //--------------------------------------------------------------------------- + // Shape selector window + new GuiWindowCollapseCtrl(ShapeEdSelectWindow) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) - 1; + Extent = "210 213"; + MinExtent = "210 114"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Shapes"; + + new GuiTabBookCtrl() { + internalName = "tabBook"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "4 24"; + Extent = "202 165"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + docking = "client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "6"; + MinTabWidth = "32"; + + //--------------------------------------------------------------- + // Scene shapes (ie. the MissionGroup) + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 19"; + Extent = "202 146"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Scene"; + maxLength = "1024"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 146"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl(ShapeEdShapeTreeView) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "190 144"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + showRoot = "1"; + internalNamesOnly = "0"; + showObjectIds = "0"; + showClassNames = "0"; + showObjectNames = "1"; + showInternalNames = "1"; + showClassNameForUnnamedObjects = "1"; + }; + }; + }; + + //--------------------------------------------------------------- + // All shapes + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "202 146"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Library"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 146"; + MinExtent = "0 -500"; + Profile = "GuiInspectorProfile"; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 146"; + MinExtent = "0 -500"; + Profile = "ToolsGuiTabBorderProfile"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "3 4"; + Extent = "20 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ShapeEdSelectWindow.navigateUp();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/folderUp"; + }; + new GuiPopUpMenuCtrl(ShapeEdSelectMenu) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "26 4"; + Extent = "172 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "art"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 24"; + Extent = "202 122"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiDynamicCtrlArrayControl() { + internalName = "shapeLibrary"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "189 42"; + MinExtent = "8 11"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "1"; + colSize = "64"; + rowCount = "0"; + RowSize = "64"; + rowSpacing = "4"; + colSpacing = "4"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "1"; + dynamicSize = "1"; + }; + }; + }; + + //--------------------------------------------------------------- + // Shape hints + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "202 146"; + MinExtent = "0 -500"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Hints"; + maxLength = "1024"; + + new GuiContainer() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 146"; + MinExtent = "0 -500"; + Profile = "GuiInspectorProfile"; + }; + new GuiBitmapBorderCtrl() { + isContainer = "1"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "202 146"; + MinExtent = "0 -500"; + Profile = "ToolsGuiTabBorderProfile"; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 24"; + Extent = "202 123"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiStackControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 1"; + Extent = "185 50"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + + new GuiRolloutCtrl() { + Profile = "GuiShapeEdRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 0"; + Extent = "184 24"; + MinExtent = "8 -500"; + Caption = "Nodes"; + Margin = "2 2 2 2"; + Expanded = "0"; + + new GuiStackControl() { + internalName = "nodeHints"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 24"; + Extent = "184 0"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + }; + }; + + new GuiRolloutCtrl() { + Profile = "GuiShapeEdRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 26"; + Extent = "184 24"; + MinExtent = "8 -500"; + Caption = "Sequences"; + Margin = "2 2 2 2"; + Expanded = "0"; + + new GuiStackControl() { + internalName = "sequenceHints"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "184 24"; + MinExtent = "8 -500"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + }; + }; + }; + }; + new GuiTextCtrl(){ + Position = "5 5"; + Extent = "60 16"; + text = "Shape Type"; + }; + new GuiPopUpMenuCtrl(ShapeEdHintMenu) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "66 4"; + Extent = "132 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "art"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + }; + }; + + // Force load DAEs + new GuiCheckBoxCtrl() { + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "135 27"; + Extent = "72 13"; + Variable = "EWorldEditor.forceLoadDAE"; + Command = "EWorldEditor.forceLoadDAE = $ThisControl.getValue(); EditorSettings.setValue(\"forceLoadDAE\", EWorldEditor.forceLoadDAE);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Forces loading of DAE files (ignores cached.dts if present)"; + hovertime = "1000"; + text = " Force DAE"; + }; + + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..41a33f548727f2fb7d27722dc33065354c46bd31 GIT binary patch literal 458 zcmV;*0X6=KP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzY)M2xRCwBA{Qv(y12q5>1C_uqHi!Wf z*vMGV&>R%{j4%x33jlFHnwSC*A4e9$a1j#|Gt8yT`bL(4K>m3kE&%aC*udDT0m#1( z#1=q43luZ4U>L;A%%H7rs>H^|-pk0y=nE6qH8gi-Wo4TQWXdBcXNS_iF%1Hm&B@7g z8pz~F7H4N?*?}cwIDz;d2xD;(Scsp<Am#w#UpTx57Wzn}VIakSum(0W(4f~Zo@z7v zXIKeh!|<C|&-{VvPa=y!V+?yb0=ek!txF$H9NwPy`SZt6MkeMbAhA0)E?zxxXj|0J zpFdiFG4=`S@_#tfk-b|8C>?PDu@Df80I`Iny-zQYmIY!NAeNMpRaUog4mbeiivuwa zBYN%!Mma2Pu)>6(E(eMIhlU9QEL{QdUm*U$f-SK_o%{z%!%{jkRQ@kgu)u=m4>p7T zA<_S#m;n|$NMejg_#ZwOA@doKyot^S1AqVn0RH4|#BR{zegFUf07*qoM6N<$g3{r- Ar~m)} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..ce9a266c541f6af63804a5106880df7417d67d6a GIT binary patch literal 502 zcmV<S0SW$zP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzm`OxIRCwBA{Qv(y12q5>1C_wwGl&5c zMlf8}u?Q4kj4%x33jlFHnwSC*A4d~Ib<v;SzhJIpE^e3+2;`p!;sOvKgiD)dHvswf zf!G4bXMtj-wu(4L7G#5d{rbU>nA4!d%P-XX_wOHHAO?%47PdNb^YG35{rjgpl5%z^ z{Tt*4RD*v0VBqB9J`J>yA6cB0m3_yrpFhz<g%gPXLGfP{gMR+_0iyVc3|<b9-LMEl zHR$Je5cQEr!$5lfKm!~#ut8qCe(9w4*Ds$|g4i&8^U4|jZ(l#3L>7Y?fHfWc1S&Xt ze9wpVODE?&dvY)I$B%DMKw_tl?!3Bg$)u=vZ(g+g`0@SIPl!SPaHb=#;ABub;sRnJ zAQk~)31{!9ULY+C#4<oEDW{~V?(QFd0Lm8!Vjhq;8IdzBBO@a$ZLq?Gpsogq{fCAL z11w#EVhe~t&il!Np3tFA{sX09DV-TA{}(ApV8H@X`ybt)e@OIyC}u#4o&QLN!|*?B s2K`41MC8JX5epx#j*(gg8$f^o0Ib%`9zX{z&Hw-a07*qoM6N<$f+r`}od5s; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/back_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..5daf816826f95e5c97d412113ac6acb6a57861a7 GIT binary patch literal 433 zcmV;i0Z#sjP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQ%OWYRCwCVl)XyDKoEtqH$VO$Trq+Q zieeK&a(jDg5epk1LVN@Z+l#f2;2T)liC9>rwn^GVg@D0i*RwDy!<wtNNMYb(vM0k~ zcV;$1Ddi0DjGTd<|7Yar8W|>&i7uEhV7&^iEo~1xaBbF(wC1L1dV=^2F7#RhgG<C0 zZ~#0)=L^qviK1xV_x-65;>c)m93Og~cZ1hmmbyUk6SO~!f*^Q87;_CKcRf2_0+Q(Z zjy9gNyo>B;y)}B9)9AZCy+&nOCQ`~<u2ofa4DHcM6~E3=Q53IPmYvsieTtFDwU*~O zr0lF|nky{dDOLV8N6TOhtb<K-Y>K!8w!u~yhEb9v_q5&sEBcs%Us|->%!bdlKO#*h zwxqj62R=Y!w_nkgTZ(4VUHaa!l9;mSTxA<c78PNV1=INTkbOqVDx$Kwq-JzYiJ^XM bUjhsO;SzQm_w^w500000NkvXXu0mjfwf(zM literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_d.png new file mode 100644 index 0000000000000000000000000000000000000000..83969b176857974e53cf3b3e3fd7ac4885840990 GIT binary patch literal 1052 zcmV+%1mpXOP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#zDYzuRCwCFmRo2PXB5Z(b6v7wli29m zB)U#Dt-(#HR0|d?6>M$MzU09dL04KRl=OmqD3n6eK1f3;s1z2`(iTIZr4UvLNNFTR z*C5n-3DIb`CfSfRyKXj_y<{&lvoquQCItEv(~-@A!_2@izxkj4Ip6nj;c$2-fC#06 z8U)925JiDff-FgKNtU}r2K4*=N-!9N&*yVqs#T;?DJ3y86JbyXs3;0N9#2i7EiElb zr_&109+<*f1D8uJ%QE=ohgt)dgM3|ru4Ra#xb{$v=OM1<#Zqz#)7PWOEaVY*aW4Wp zI#E(nfRRVlG;#m-B-(eqjK=l;)v0}Kn_s(#f$$kz8X0w<JpJj%-+YbTuk6P3_!#~j z8%Nd9ac%elEUq5MKl=h+U&D&uT(P{SUcvdkZ!s_!MQkDgbv}p8oQmEPpFs4~L93d8 zB;$p>FX5SB2ZkaMbieT$7IQiGO4URh(W_%n)dD;=P%m(>=~^x<!du5dD_2mY``^C* z6;rWEG;Q>QGYoVed=qC!uDVmYerpHj3256Eyfjfy@f;c{*ZJy^%W7=aU{<TBQrf14 z#Kbi2-ihJ%-`6qN-|r5};_wG2SkHk935`@5l-<22Q7-0b&?Ow`I)Jv;jo7@U4Wea% zvus#&{rSvk_l*>K=Qsk}HsSK%?~qC5;rEWAp}7gMTcb$EXE6Iu44VV3`0K(jMCxRT zs-n}6-Me|qJv5t)BM@lDk@rJv$WT`=;bwFM*@tr|s#z2>3z!@o2e0W6sLT?nlwhOE z%y$b-=N2$OmqdH#bBGRIfoW8sQv)r`JV1fKh15fs`2s;T2PzO$V7_s1?<psFX#}#) zEXHFKPVU*i2M_K~A(NOTr7G-F8Jw<zRtj*WkmV*taj$ekpdAB!VO+U5f}EPe_MJiW ze%J%EPy{L!aHJZfQr8Wb<f&yXm37yv``kmD+XA$_J&p0<KT#@c*wE@nIypzBG{6&x zr@5q=q0up+TT5QnH8$bho*wt*W$|{;SG4>I$dl5cbAKR59_C4Dfm9Z#ffmW<63uCu z{G=k|qf@8QvO;-R1@+P<|7HI#P_}nCt5Au-DjF6w10_<(fF)8Wk-|@Z{0WCbA@^(V ze`D*8Abz+M#piwB;n~jTVGyWd5s-CjLnB^47Q)#ZHy#UgJQW1tf9;2F9X;yglLRk` zBGx*1I)|=3)FaDrIu_DSV%HqTI23VF_ISFI$=OIIlTp}7jUAjEpKEhEj8k?!5?}yY W_pCMJzz4wq0000<MNUMnLSTZhB<TqN literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_h.png new file mode 100644 index 0000000000000000000000000000000000000000..378c734e80d5113a45fb6ef92dcf4d0450c451df GIT binary patch literal 1085 zcmV-D1j74?P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#-$_J4RCwCNR&Qt<Wf=e6C26m&EjMdM z*EQ>?EX^jOvNHR@eeD-T(N6(~y7^^n`y#?<CoG<}4upMi)QMyGLC{wGR8U5T+FC=f zvJE%ZHKXa)H7s2(&E2I<a!Kxw&wFbJlU!C|sW1G<3wQ7Hdw$RRd!8Kk*VI%ifG|Ns z?Uzaz_S&``-r3P1`u+ZzGs?0o-kqNh^F%7Pw6uVJYmPM@(Q8>27&wlrxl%^Vxc(o~ z_049HGB`?2({v>@a#|#QbqX0p!|oTl(b=^JSw)6f%EO>@iFg8gUh0OgUXY$hdF8~7 z$ai0Vg=p+DmWh<-dGrkqpu49Vb2HP3Pv3-5E+hKeCGZ|0a^yrGOv{!YN6MRPO8<=p zp*}Kl;XFov{uQ%xccErfET=OV=sO8tT|G*M0p9Dwi~INC*-#fQj*jBc8?S>>k5r2H z5YC-iX2tTR?<EoY^D0y|3m-je@OddyK+okN2wvy~9l6z2{P67vZqLqPE9-O9z~Q&v z#JS-S_d6v%-ZU#K<r^9$Mb+pf3xdakraB+C5V6?Qh?Q)?Nzx#-46I}ecjj?(I*w~s zuHg5vu_|h*yp~IO84eyf3W^R3Hp-R_lQR9_;|TJ(6)Le0y|4D7ZF?&Mfp&PP3LJsO z`{v6b*O+TYGF{#K?xFV(*cHI|#W8rvMB&2+(JYFXxqcn@7Lr()O`tun1F>HwK%)&F z3gMw!o&Nip6SK0BOnBGJTZuHCN@7QwA8)_^j-yz3svehPF=XU4R<$fv6a}+4W~i?W za1_kr5L4OdtQ#rgtEx1kEJI#e#O_cKljDDoPYe`wolLd}Exim)mQf~Sf!c;=tw3jZ z&dIN;Qdc7}czgnxMx&S|a%jSFY0rTkByZn=oJx^W4Q8<jo07394o5-Ry#_iuP|ZkH zC(`!7E__eD8Xb>9BPWNR-;2-sKS4QPfK@8NCRn7hM86MAVms$ve&s+FrEBG7n@Vdb zqexFbvn>*xm_VNT(%-rbOZS(+u_Reg!z6_d6qH%TYzteOn{m9qA4)b`G3Fyia$Cnv zo<eN07g-t-mwp;^`j$DBfn&)!4R6Dsy+Qj^r%xlTD6XUMv3<%AzjaIc{QL!U27}Pa zMEODiIYM49qCln9%La6MPiShwr$a;7845|&;yJT-ec)GvwwjYZ92k@azc`Doz0aXc zWi66<bV6NYBVG%K@#Ewq+Jix9Bk>*@X(Owxvs3!$%$bVMlH}%%tVjOZHf#N7rB9S} z)0I-gs*0kBv}*sQt*UH5sEl-YF_j7vHth@l9{~mcZF|xkT|op!00000NkvXXu0mjf Di?jI4 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/collision-shape_n.png new file mode 100644 index 0000000000000000000000000000000000000000..743bc4091e29e3e0035fffbb1ab90054c312617d GIT binary patch literal 735 zcmV<50wDc~P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!fk{L`RCwC#mcL7CVHn51YK39eph1B) z>EKX<G=$q(1jhym!p(m`oSbrTbaZMdoyz8*CC{J^>LQ9Df(n871AkPeW}4meJaD4B z+#4sj_j2il7Y_RJexB$1e7_!5Z*On-(@TxNu)d-HA9U$=2RfaOSSS=Q7!0txy9=w; z%Fom36tAzZs8*}k+uMV|U=ThFRsAY55==ZhJ3}UuVUSv_#>vSE_V@QusZ>xdmob~o zFdB`pyu1vb&xf_OH9@hwFE1}*EEdDf%?;}HI_~f9(d+dP3<j~XvcfQpMgzOu&fQ;M zU*qWL2#=4CqGD*P)j}?p!^6V^U%tA!%6l@IF!cHPnLE6^yu|tWImruB?fCduxsi5u zb_D8fu~@Kl1k`G^SYKafm`bI>d_Lz*XYTIqSnhm2k9a&TDt52e>*dR-12PR=9tZ^R z^z_7qIGs*xZ*Rk7GVwWDbmsK*RJoBPX~je$f!o_#mYAg5*w{d+R6?WCU;s&dadAPj z<YGv6>RuX;peQml*R8EBcsw5NgyhX;vuvp0aLDr}7ZaR{r!(a6FXgn`?NUzJovBoc zJD_~hXB44+zt5c!lx35k@>V=kl8$7fc+_e&j?Tfs0h^}XZohvg&-;T3m3K-8iAJMH zr_(H@&1OR=6yk1W=MqrL^iN=i!=W51Ia4qijbbvH<a};!ZnCsAKiUMLO+Xo{*X!Z; z`<1(wmgGqB@bD1LW|O6)i$BgxE<lEc!(q7HZsALB8Tu`aL?W<QENmL;csw5SGs#TS zQZYK6?o+@|b^38#U0sP3{9>{A?pDgJ%jIH8&1SRk<+sfDKZkGVKMnmMzyPx?nbW$> R0Z#w`002ovPDHLkV1h;3Xwv`y literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4bfa56aa32f77d883b50c844535c26e2511bf7fe GIT binary patch literal 874 zcmV-w1C{)VP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#3`s;mRCwCFR!wLVK@@(oo81`1N<`FS zpk4$Ef;SIJ?_L6m^q}b3n<sDHrHZ0dy@?*`Po*b8s8}o(Z=&Eyu;kRDwM~<3vVXI) zzV|khHes`EW8<V@mq~ZOdEd{Q5mQrBrvP9Uza<#L5JG@ynvl!oz_#p)Wn1MO0T%Lh zX>{~B6pKYL3<HKW*4Nicf7YwB1j>LX$Hw5m$Ox}!SVKw)@8{-9TozS0Y(N_He#^3e zL5b2~1L|St^I*V#9u^jtS>E{78Bo1$#7~T$e&gfQ3)yZaR<r&gQZXz%ySU>j&Rv-S z*Y(<RnH-f=ZL?kyBd9MU)<DHEg$^f^tJa0*$rm^m<9*9*F<4<=VK7jIpn)Io)i6YR zC7a+eku9JzA4Lj+AOzc{XH|a}XVNPbG@JfbL1C+$31FfzX|0fz9YrTQ%n>wNfi7j+ zmxZDFcThiwz-fBiKZ!ci6bvoe)DzRDX(VpOKR?`qN?TqC4aen|6PKDunz;4&dnY>j zmqUm5!^F)`eE;sz`N2SqpanI@OPtAG*`!!u<qOZ#<8=1&Yh*@(k0wluPBv)V1cx+@ z<5az!rn?!hBv?$nL<72>4>F2Uqmv0L!aBcLZ+F_fq;cR&_`T-vj1UI1Alg|C{E&AP zidkB8vb((WAP6PHC0W(bfQt#>*J=$qLEXYev*Y*yG*w{>?%jFW)25Q<AVyBW%4(gf z)8qd15?_xVJIIE5b9a3Apg+Dn;a*CujXF)&W>9_zYa32?PLcA(=}UZnX5t026R8MI z6?N&l?Iuck(gW1mx(fEIFnRlxRyiU4$Z7ce<!5p<sk-R!2pfjP?QEBzWO;I6uOYrJ zPI4^RrBb%5e*o96FLk;XZ3YS}?R_>!9VKhf3j6IAltYSz-V7Gs-e5|pLKVkZg$#a7 zNn?RDBuc45z3AQ(W)9Dx1B0fqK&BxL^+NQ9-UXVs^OfpGwM4q?#o;VVj+NzQx>T@I zyIiX`X6NTWlq}1}Q@8~tuS^W%Tvowv`L6&20B}WnoJ6Wt4FCWD07*qoM6N<$f`C|+ AUjP6A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..8ca5a0b1c21c3dfc94df37591523b1e152580d07 GIT binary patch literal 1040 zcmV+r1n>KaP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#vPnciRCwCFR!eIXK@hHf>?6r48)8@w z{s0f+4-mWw{sE2g;MtpKP6`Q0FbD<@`VaILG*Q8mD2R9qNJR0b2TyT9cEdhqcc))g z_3UnDCbNkMn}()mrf2G_`s%CdVPAH3P6NO^&N7UD?BU#y1X!t5%BL!ou_OMeR?F3D zbzWL$%hNM6;EWzSDB<k>-YrY#0E{u<j)7Z?lkgDKfwcyJ@$rK?3xp?~wO|dxbDMbK z;p4Z<>F61GdWg}xw=b_A3Thn?p0ostk=i7t_h@bD%0EZ3xV8n2MpJOXofOhZDO@%% zf+99XY7T5V^s1)}2*JQ;^oX%2A-K{r8vZ#BA-RvBR;%Un`J644N{X{WCMe<gNDWQ~ z2O|x`5IhB(58RBdadr3w<NdFC);rm1x8<ar(8Xer*<3CIupcS$Y_!GzlCaiT=ybXx z>7=(AijTy>X*JeQ3czUND*`eCAv{SQW<?r-hA|%xABI4^I-@lcw4XL?4#wvrZC0bV z-EPn4koMzDI2V~<M0IrIqw}dU(Q4Dm9F~oVBGrZ{N^Yu9Lp#W(_tl$kLnjAiG<k9o z-hHet(DjY0=hm`75h{w*Sc(*LkbXgTpp7}@6sdCwCJrm7#rvDY5Gl_IEFY|)1c`(f z2`M9|Mg{nBn4Lc?l{5<$9U%v4uhDGe!m!9oQ<IA2nM<mdghYv_q^H!|>o!qHJ*<$F zJKnH{TD_5=my}GljM5Y-Rx)#iLY@gJ;}rRKd`<~Htx&DHC??$(l8b!mJJv%6>U+&h z7Q&wL!W)Q*svzBThHdb`$6){)&o(m+&sNU*NLlQVc>m#tUr$Vz;L~<v;nKzYy06`o z?Wj5Ux;j{gODi{5u{nrlvzbS_WPESy#r8s@8Km-T;K9nAzrMHp9F#T;I~o^KSRwYs zWIesUOJvs+3IiLrw1t98oba7s4>q1|#ma`k?3wfM_4`kMAGA7{9>u%8uE^zb25a2K zl`jVcJ)+mSiHV}ZCZ15!tvhpTN@*Sh0je|<^z8M^&kIi;{TgaRsTmZJU`i{7Cr|2> z>7Q{U`YYBM)mr8O?=T8Jh~nDy3+tFarnTi51dGm2=i6i4e!mU6%$y!II66Ztpi#Ep ze+>^+C@{bN8RpVr$+@_Fx|ZNZ_sJuMBZ1(IQ77<3lV;!ubtGaUVc+$Sw*Lgpbc7h- zsdU8c`;J6Ez^zuRJT)~n_8+r$yX}9tNxd6;_4+){WBbGZE5HC)6`*g#SFY3m0000< KMNUMnLSTZi3F;mI literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/detail-levels_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..f7788fb21aca97c2519d52f6948f9a42e5bd62fa GIT binary patch literal 614 zcmV-s0-61ZP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!2uVaiRCwC#R?SYrKoFiSprqlZ51<K} z@EGb#7!AR@#<TIn$@ml)gA(zi(U|xM#3S)$f|_tgC@uZly1U)hQYhU5YGNF+nQnL4 zZ@-yuCPj|pz)zb5zhM8M-)iZog^OA}vBJv7M<{CT<+T+LN>yFl-4+I{US3hRNCqau z<~|uy0t=>PfrJp?P+8G;)V%lZoIU5ru*guS0>#(?%e2T*4j@3WOzD2kG*&CopbiGp zG$#Tf!jPr`v1s7gg@kPkx?%YFjb&=j0o1l_&~@EsGl{9Ue`Zh`+QC2{$tj&T8c!qp zWm$&u$!VLHPIf9#!_Yy~G>{~Tu?e!BR8G8f(vwOj613k}{hW>}6|*RW>zWA)GYpxZ zttJFj$iV}@CV_g&mUxTQZ(r^EKx~=(Xz1x0u?z>35`|YMOy~hYv3LNcX+XEzb1&l3 z3~0fK6%vY4=KjH<6SbCKTZgU94G8v(X7f4RhnI;{=>3Iz@8kU);vFUz=rqihN_*{| zqKuSSskp<Q&#N`BE2X%{g1y((4DY2w_R;CUb-n%-E~4;4p#ZJ7cQ)5FsvicpKqH<M z)aD8t62!ypO_u*x=AwaIuGFqn&b%HRW-6A`{K{&a9x{Q_hyv2Ryb9%1z{W+v3u%ha ztwePyV0g@d63?L&*kjKVRP-X^b^7PN20sND0F6-vrxl7xQvd(}07*qoM6N<$f^(N2 A00000 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..0dd59b8566b365ec5ac48acdb0613da408d0ea6b GIT binary patch literal 468 zcmV;_0W1EAP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzc1c7*RCwBA{Qv(y12q5>1C_uqHi!Wf z*vMGV(EPuyftexH5=IaPip|&7GZBRGLHa=Ikob%kE@EP0W?*J!Ij3)AIYCujM+7Lu z3=~UXWo5eu6iWp0p<*x}ff!7T=+SR%Zig)J=+B?uWh^YLE0BC}_{aBeshnKgH_n|p z%mFp*FAJuNP=w^z*jQGeDY50|;lBZ8v%}2CG>DmnlpqEf^cRT#kZI8WPr&fygBtV; zYhVKd8jcwmnXY|&|0Wh$3>Zdxo;|+r&kV7U85V1}T=enxub*>H9NIP&BxLIn2r}Tw z=a27NFP=HH8{{N;Wi1A%xBg=fBVe=wU14?M^ntfPE*nVEzrTNH-@kLU_u1pS-=NC= zL8JdC%rK1TXJiDs5@Y};G;05W0u$;5n8kmg;{TxHzp#buf2hKrP(^>Cm=PK#|Dgu_ zN6KdZuo(n%!GAPOj8Jiy8U~~+@*kIr{vj!5Kr#rK&wwlj5MThrj*f?i?3VNZ0000< KMNUMnLSTZR=*?9C literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..65147daa61e50cc32351738df80395d5496f45ac GIT binary patch literal 499 zcmV<P0Sx|$P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzl}SWFRCwBA{Qv(y12q5>1C_wwGl&5c zMlf8}vFLwA>wH6~1&kmJ6q{e%FhdZ=2k8N+L*g@HxaiOCUkv~L{XJLGFmpmwO1TJ7 zh#4rBz|72Y4=9!h;zPw?dYNICG9t%XVcpdKNG^T!<NMb#PA;AmFtLCC{vCez`gy8| zxb%$)%{d%UL;iv=3$lxT{rrK<l>@qD1)7*GpMdZUs2DrUPLLfa2L1f;gOuQ9hq~qu zh=Xd-&+o)U&L>7jMn0%Pzpw^2$jLDL<J;G35AIxzMHc(|`Qx4orw;f-y~d0jYiKS4 zEBN^H$M-oq*3X*?vdBL&8|aFEPu{<I*?M@-y4@g0sc3>y2-sWyp%~Sm-@kq^ynFM) z>cqjVZ-HDkkfLv2KF+>*^=$8>d)L3gA`hevWG|9o3@FhL^g9dG08Xg4|3L$j5y{fO zQ1O3IaZot@VL=Z|sKTF6MSr1~5gI1{p$7a%%3@$Xx<N1({72Ko2#aQ@7z0v3W2Pe{ p&HvEE7}59)=wgi2D%b!53;-RM+2RfE6ea)w002ovPDHLkV1k0E;>-X5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/fwd_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ba5f67aab4f97a8bf25502f6d8de463db89c4ec8 GIT binary patch literal 438 zcmV;n0ZIOeP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzSV=@dRCwCVl(9<2Kn%z4yhH0KNJoc` zr7gG$K7&sn=-?~(29AP@(<iw44!XI!6s$`-h)W9{EYy!muV?a+gILQqXa^5Mn~?h@ z`KMG`YsDOAL(FvkA4s4^&f++>Q55Y{0f`2&+b|4woR4=fkNI-4BPrz*z^;NI*acUC zohYRWh@FzJ*fh!&m3(VSlJFJ2_I-b@wZ3EBKXzSr=6T*rnx-B>44Pgu^&W>%w}%KJ zcgyj?hQEl7AnoM&4X7D`<XRBu@!T}cF^?Hz9;&K3B3JRPO=<KFBHv_Lc8!H#)d+oC zE|(X1p5J2)-}eQHY{`Ct+O|C?ilT<sO-zC=7G+sp)^+_sDFZz_1{2@b4_na$*ru`Z zQcvx>qK4uF#Xm7;+>xckj*<+SBuy*<Ec+-|{utysu%k<o;(RnG^eS(vqk$z02I9UL g$K+f;wl4t&0IC<09v3v(AOHXW07*qoM6N<$g8q}b*8l(j literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7522ebfd46479a84287d3582377de8b1a51aa6 GIT binary patch literal 981 zcmV;`11kK9P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#cS%G+RCwCFR?SZnQ4s%ax4R3pw6qjx zAR$Fe&<F$-Aw)t*Bt}S-V4{hM2QU5$-1K0g(Sy-=Koi5&E5s`XjvhToqLA<*kk&%k zE#J`YcAfXyZ9z*vOv-E~-R*n(e)F4|of%<lY^(#o1Ze;z5J3<iiX!ZGJ7h^-mSkzf z&H&35IndmE9v+VeHk%El5>?#{L{x2pfpWmswl-9{T->5kiLUGTGBp+8v>3xu12SRf zOOgZ*%3NA%KucsrfemLY5|KM|P=R^1m>0s~2!5@Guo2M^3~fM*?x5Q1M!nw$SEUm! zmjksmUewk3a>q8;;`iBkgx6JUY-;!&-b6gEA6qrkciTIAToD9*{9MEl?X%lOJh(Sl zJm`~`AK-Pn(BQ8@rL!UlWoAIxeIj9yCJ-Z_-Q9ic?(NgF$&=GC3|<_pWGx6(Bx~1I zjjK$lUSC@cpT~`|GKB-`bf~IrBeJ>0fm!9Ts*p(=VzGGY%Mc4^+EDAm;%bl|a1@JS zX)VOn>#ij5D*nIQ<wP0#3^)@r?G2}+3<h~2W&VkCb7pUKl{*tb)8xv_+15^)QPSTT zFw6ZF73KVaU5QLg@-y}MJpcS>0kdD><Vv<`4|drEp>U>6jrBQJO%9{$M%s|$VkKTS z$qDMTUKEhdLQ4(~j}Rp{uY|&gs9RY;9hndm1KQVn^~AG58M;pirS0xvff8z6i}Lu+ zEv?||w^__BtdIfG{DnW?2A9KukgDR4z~Y9HHO7pMVI;6YMlhAhpq{SwG)tkFm@>K9 z-_057bm*zkO=ULIi{~2gZ1gcMcU*v(Nc<nuM-PY5b>$MCzZu8eTJY3;e#-Rw!vnb1 z*~&B4O0H}%EvVqPv^3%4yQi4^JdO9CCUHQn)`^8oYl<x4?qENLZuKTzC)lWy2(a_H zNaEG#?-8gVP-ZRb_#=02pr^YXFJ6yho+{PA^-kO#>Vua$4gVrp$x97k$ze>)I&;yU z{4JHe$W$b4sQ2UTt5NDWabD0Zb_|5oYKhpfGp#zDeQXg6Z>bDemTmk{2E+y}7CRd~ zODR|80!xF^99neuX9p_^#Z9tPhMGKvV#*r@8L!C7vKH02)x`({VDpYlM)SF+0Kia? zXa6!Wl}EO=cP3_LrUQ~B^S&wa{wI`TL|P^t`6IvpmiJ0hdM^`z00000NkvXXu0mjf DSU=25 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..efc103ef872ecf8ba7abc68b15fe03c6a8410ae6 GIT binary patch literal 1168 zcmV;B1aJF^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$GD$>1RCwCFR?BZ2MHru%-SyhLeuX@2 zT7eK&o3;cADkKO(LRAF^4i!BS4i)0Sg#&+5C2qMOP!A9!)FT%Ji4Y11a;Svb#JK4? z_CCCinc@3pckQ*~rtQHU>yy`>neR8h@9~?Zd%L@BLdX_alPrGp!M1e-Y_(d=l~!xv zh!4BnX1Ckj(uEYwrR8NJd9rXp`VZ0b@H!Z|1MImA|gAs&EAA_OOdvc-c+K@d-u zLJ+|Lo{7bS3P})87YGVW#S<_Ok9wtl4(`=@y<_XYUa#hPzE!JLy|vYqVZC0B8jX5* z{#-kFvAr6d4Gf@w4NUo|$zON>XdHDP+K-&Rb$sm9An#f}C$kCw2XyGVWwiSbj>!I> zf6F9JG)>pVtxrEbm<Nh`6;lpIXC`ldz5P+8QgNG&`lE8$45ggfh|4L0@`TeQNi;tg zm4ea8gh>iRL~%?t#?&tOo$bq*Vw`!Bj3@HJ7~`JrH(bwa=(?8Fs#V9fE!WWXI4ugP z4Tr8h7!0dW92C}%69m6m;0H|8GzohperNxyyh0(#xlAEn4ulpJ1KE_E{>d6z*)-j% zZ4CjGR?6kTFpL<P$wDYh(~QG7W(HIZaFLS;fdL*9lB<dmGiE_(^Oe~y71B`{)?^wo zndVj^16nlGG(rJl17W9}PkbsIz*9^Um|*l2Q*XKgBqEIqNIgmgm>p^2Kt&IHVp!9V zimXpFrd%LXu2iXL?bfO@E}NWF4w5oL$6Eeh(V*}m2|}QJiDc|k;ee%uG$*H(N;!I* zHbP~VV2|$mLDjbH0p!@7+G|y!lqt3&`Yu*47LpWcpUzb8yYIZ&{eI_ehKmd!DxB-! z#25e|V>Cf$VHnjMr_X_%3kP*=%W^Sy%5T<=W8W$gBF!nHlR30XCBqMbU@76e1nV?X zEW?6Gz?T*TqXsaqpPUSnOY5C~rBXbRrkIPVe6xuRp0@Rioqgy926@u*W62y$PJ`oT z`GutiH*b9S<7<~Y#|el7=JB+D6bB50oS(d#H$M35@_Og++dDhg2Se}Nw46;IpuPHU zUESE*xN@O~p;R@ZLP{0eqVNb6-a<*w8=Y5Q8hrKTXLo+O`*Y`)-+q4|PG5q=V5mW4 zS$yTvg}wJSuine#PN@&bp8x??!XS%nm32U>y>kS=um~OT>(@39Uf;NQ|C{f2t_=om z`<0i^?{B{S#sS<gAvyvX#B~DdfGc2l#{ILt(_1FH<IWlk7#O<2TWc%+7oUIfLlngt zs7-e3Q_xpkfW``tM8PPNO6X@877Izod_FFxvjb0$Aw(d`sD}AOhB-Ty-707K!#SN~ z{qzh&fgm$zHMm{q1ng=2$n0u+M$qwg1bk|+!Yd2D_x4@aZGxOF{2j^j)E&~t>fP=; i&KB5nd&B=<fB^uUUpuADvb3`R0000<MNUMnLSTYDQXL}z literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/ghost_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..79d2cebf7cc557692238a513641b89dae6527580 GIT binary patch literal 683 zcmV;c0#yBpP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!O-V#SRCwC#R?SKrK@gtq`7sdJi_wT6 z8%4aS0o{w<bMjHV`2Ze$fjoeBU%=c%T@lxV2wp<6Du}u$g6tZ9W~SS(-RiX+x7XQ0 z445>0TRq#|^;K0@)eMtTI&VF=^B2Z{()UN|*%pSs3_f}OI$6j;^aGzrb^~X?F;FF3 z>};2+&Gh{`*ngQ6BzcG)gCsDPnU)w`kX^<(wJ!wua@(F_d<RZ|GH|IG3^Ima55hyO z`;L@3$Q+(stjBb9x(Ud%q<82W%G*5DUqe|LIK}fdt@|npAnFC7B8bQ)NrY-$kO8Sl ze*@#3Go_#t7z4p56jv}mBE=WLA5zR7gU@RCb?GT?CwnW#{WSpMKAOwM|MgHez&XE4 zoJ)PD72`hGlLTUnCql-WoR<btds=R>rakbR5yIJO*5*)4SZ$6@r=CC~z~?5UHOsly z;YFS8j!zVzQ%Wiowf6(?Q3|2t4JqxzrjRoG#HlA*wvJS{*g1z7qU;SUVOGRGY#V?a zfB`C-i?$j~(|eattL{VbKMx^GW;fEd6m)dVZ57vlw$jaPUk<wW7N(~2aTG0J--NMz z4$j*LH&pmJKMaCkadT~Tk33Vg8L0+CQkuQ@MN+aj0Qt8l4Cg7-my|45p6@ThvlZGa z2#P2qDUuC8&8tmW6_z%NKU~k7fmf^Q9Og?=9M2M=_AaCZyQR6v6y8MAxKx{gk%Zc_ z$RHA#UTqeSBSWcf4BErk=2=@MV;RCo5M46D)(BpU)G_Wb(_sJ1$M(Gd0{|&n=kyN` R`j`L!002ovPDHLkV1h|=FUbG^ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/highlight_material.png b/Templates/BaseGame/game/tools/shapeEditor/images/highlight_material.png new file mode 100644 index 0000000000000000000000000000000000000000..78ac974a35973a99eb23793da9f84f3214124c83 GIT binary patch literal 513 zcmV+c0{;DpP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF00004XF*Lt006JZ zHwB960000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeRH-mSE0004$Nkl<ZcwX(B z!AiqW5JjIL^aEV^8?O8Zzfy4N!i9UcE>cBWw5u*EXrUmKqJ~0K?<6lJ1{W4w2<H}t z_ojrvT<+Y=O!5w@D%~dFZ@W#03VV?H08o6zc~-|t3AiPB8?OQAG9XZ)*=Ca_ih1P} zum<!VgA_m%=jrY#K+FG#R~j&)YrqM;4ln@L0C*kX6tD=k-5|6MxTn_v$Mh610Aj$f zZik7cUSO!W-^ufUNP#CT05KyJ024X@Q>+H$nLtTrz>E&SGaZ0ev<65A)^rBU=>Xt0 zfL#Yj13u{~@Mr)i?*jWDNaSXyVF1-M6X4DRG6U}D8h{7j2CV_oz9l^c8U}Pf6eanJ z)c{xnNR-dd{$0vD1*&vS7r`sE2B;T!GXvlv*rWis5V8iC0Zsw723*kVfCXIx&J6%u z1RDlWO}&6MAPI#+42Z)6wdK=P`}oCwE2S03(J$k<3;^N?X*Ug4f8;;t-cwnb*Vupa zV<MyfmU%6-AIm=S8vCF8QrFLAFS7c6v9`94uWM&tIyVg(sA}hd00000NkvXXu0mjf DT3F6} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_d.png new file mode 100644 index 0000000000000000000000000000000000000000..98253e586cccaa48797148360e4c2d3271459583 GIT binary patch literal 1094 zcmV-M1iAZ(P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#=t)FDRCwCFmThQLR~X0txw*NCi7{=& zrrAq-Yin&SYRe!LoOW!Zt9}?%hWNt9hHq?ai1_kFT1s0OV}*?|3fabnA1wIASe&!S zUJ!=1POH>6oVv!=G%==W-rA(e%{@EkmIUkCr0I+gJcRpl?l1rUbDndKs=K?p7J!4M zh&+T!r2@~ZX=)&7G)|2mH1iBtR8%C^)YQOeG%7FUD?A>L_-J(0!JrCI6h&yY+Pp%o zRx7+-ugI|rrZC^Yl+}VDfJ<HE8<+}O*BR)G44&t~XL_NfrA2Xo5Q2`5j`aOVEP|!F zr8$jBTg-8pp=@w_d;6?==ZE_6;~(E+=j-odQ}r7t&=<^lo&vSDw$86M7i$=>>&zDr zcmYGB*KmFK8rGNAqG1h%8wzK=%aqO+8Eg8q$`r85Rz~d&gI}V?T3aOoiE(^$?rZe^ z<v?I^;y;RWhCU12(YPJE>OTOH3A}C}Dwb8Fscs8CeCs2Gr$ab?;U~zloKfY1pcn7{ zsQ~wH`50U4wxL|8M!B^dk!Tq5B!Pi5!eX$Z+4dg%VLx2sF33rF@%FNK@c2F&Uu#6u zy3Go}s5YT!SuujqApD^Ts4&yxtjkvxuf(wD0i>j)DE?pePQ|7m(|^uvm5B_|B4R2& z1$ST!kz@#y$q3Cb%;rM;?YTPxB=b+Eth3iZ`_UbAV<PbcdX*93SO~6(M+&g_(rK#J zk5X$XOoc|s1=AS0HG=7QR9OU6WbTzNE418FfzvlbxE;NYfO`@r`%Yru-T)*yfnY3v z-a!Xe6<4CdQVH?pQW!ZC9{NU5^-483j$3@6`lEjI{N971%Xe_!a}RIUz76}nA7Iv- zamRZbJp;$ld-W8~{qi%dw*@A>36(FcRxDn8O~<G4+piZe?EeR!?fL}vPrt(og9QSo zL&b_!SW{AiNFa(=%_TVYZVUD_>{sf_>OWUqvnqWY^k8uK3Sx;EmKPVm`NWB{gJ-az zY!hvdF?@ci12-Pt!0wHEaj@x#QbS3Sl;2d(K3ms}O4<6^xULD#Z^v-<=y@F2a1<K9 z9w+)vpzhFG?EGpQY$f%$c<eF`zH@lygKR;klc?*<F7RqT_lcCU#zYtOja3b>mDNK^ zNNL-i?!^>LL-$LTw86lsx!j>_08`5VZEbDP>2#2IDc!HhWHN1^vQN_N?ChL1e$KGl z?ZoABk%a_RMkt8Tnh~m)BoYY{kH?i{_t^7n(cj-scn!bs-R*fDYdCriebFZta9Tld z(qm!P?dW*~<Ir&Of>zt?c8@uHKA*@=YHTv7)oSKtKDbVr&HoB807J6+oWHP2Z~y=R M07*qoM6N<$f)U0V4*&oF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_h.png new file mode 100644 index 0000000000000000000000000000000000000000..aa41d5ac7bbe59e8f0a02ea94b29f99514aa5fd6 GIT binary patch literal 1230 zcmV;<1Tp)GP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$a7jc#RCwCNmS0R;RUF5E=l0T;+HPH2 zDg(D!#0`iF3|ZzdbIOC^k2QuYLybxnqQ*p@6cT(uX2Ju>)EDEU%q<LqFp`OL8-Mn) z;g|s&j6aqQ3NxiGEiKT}TYB&H{F+v2?d_tm`b&Q~x98q-KIi*A=bm%Pt&x#T0H~#@ zh17?KW+y9(V(s32`?MJu87U_^olfnd)mqC?s8+An11(dY5ssD>1OYG!At_giU}XJ2 z(C9@xw6(Rh8SC(VQ&Uq*w0_02A~$Fa!9<-(wb>5E3@hb%{x4Ac-_XzyZix3W&RqOD zw|D5m!HTlV+b<n8FYM093T$uF*w`3;zOAjTIojrL+l%$l_~!fe-19$nl*wc={?7bl z=H%Q&W}d0QZa(_*e3mg59_?HTN(Kf7J}D|HN;;=!`_ANcUOrz|bodGQ`Np`cw6J8M zq_h|uRyWMf&58#e&0nU<=d<FJCxVWTj{_qLeAqszM1=^fJN_2DSy@Nr1F_qjih}GX znG-M8l8;U{5{4m}Uv+(1%yAqEg+fXDLmtEPg5(1ihI;mXd!@7N{Wm^<<K-v7q<fs? z?8y-}xqzJ8*aEde1KIjV$jMjg8SlC$ed_L=9f3fANe&i(i7uo>d3R|}SMgjWR6Sb_ zDppN2at(P{p9z}*ztXYdK-FQ(fd2Ax3dr=mDc$DgCKC(>iBzA+qtGN)&<=h64G=_u z(1$U>lx>1wD41roEx;P*B^!Y?Sm(T8G#bIOWYHsVYimo=X5_I^dlM}rrCb@DciaQF z&qbB0V9mD<_Z>^LT+{Sj>)WzB?XLYf`@vw;1E@n_ciVTaZ>+I&h2zpCv=rkP67txn z(nL_xZj*ar=@*!BOoD6K1LwXw$5<9EdbwPl?hE>i9hW;mtJb=78l5}CVDQL^lAE?p zYfJK;c2c0kEAs%QXs3JK1?@fUFf}%t?)7=~#||B1PJj40!oc#I1$u_M^p~#xpuagW z0xE^dmo85aJoQ-N3WX9nI4Pycp&P4yX3;ukATm(BSNAT|o8JMQ$^Z;u&<QwT%7d;; zU7%Jz<bA%l{Q9fquTS8dB9vj6n0~kINPNQG1H<OBsU541EMDMQ!!8{xE-%8>;VV#@ zcL;1N%kah7&tPnNlq<|B9<8r<`vwC1e!ncdLK#MgfW=rL+w6^x?(Xj9nwpxH!v#l9 z|1>$AJ!_rGnz{X}@#gTYopz6XkDN{83Qa|$&99vr)oONe2*QQKvMfJ1IM`@5n?I#> zV)&KX7BTiFnslt!?a&2YI9R^$O#YGkbjLvzBx2JazM&cl6O>9NuTUuXNS%<#!I%v{ zKG3S(-ri;eR99D@q8*0tRX`XWOgKe)P5l$o($WOX{{H@t<Bu^(Xd<OmRaG(PA=-w} zhyx_jLAXR9s-=T#`O`)|;W|mGRO+BW2j~DqsRQ~!kyL4_;-4t}cc9b~&+6)`mPYEo sv^^f20uMqvmu<FMn*Z4s{_g?|03#Z*Y#vt_pa1{>07*qoM6N<$f{VFT?EnA( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-bounds_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd3633e832fa5efbe51dc1aa6b7785311fb3bf4 GIT binary patch literal 861 zcmV-j1ETziP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!|4BqaRCwC#R?kZtK@@(oyU`>hCSngI zG_9D%QYaOhG}T(~y_sAUl;X`>PkKo}1rMcvfY@A0h*$OCMW{4~9tt8o)tVO3RD~+q zG$a&%B-`)FtZYkHOr$LoA3Wa9%$xb<`{vCgLf3Wr+osSzuuITCYMRwRJRV;XLXa%9 zu+cv^H|L5*qeXMBsTfb^q|@osIcsZc=VQu~84NKzK0a;$K1O&sJ3AYNdwFqonaeSZ zcGzfF&D6{StPQ|n_@Sz*?d5X09ewXG{u<1`S}?EfOnJv+V`FB9!(Cln+;n@rUUgt# zK<n)641v{CjK{#d(Eww{HG#53iA3TQaA;_V1_uXOH?g<3=ZQoj>d455l1imiY~~*F zpWqx7xVQ*Y!tNh2AQq?I-d@q%+$>sJT8^1*sZ<gT4Gkg?2#AS^2^D=!4u|6!qRPNE zGlkuZ#bVUc(<88;aJgKH&*$R-PcD}`$uYd6qXR(%WSuBAGi56li&UvpSTcnpm~po= zo6S-_pBIHfft|P?ptt0jLV8WrTio8>7Fe8|PA7xV=H@2%`xX`!N*fy+*-$7%e!rjU z>+8Ayx~6H4V;H_Gy!To_KjG{Mti;aF4keRGb$53+2$`GQZugzl)m4ILk@vB#t}cg| zx7lnrfcgF+Olg0ApQfg!^p%yBD`*D0ySuf~(NRJX(fay2&Ck!bE-o&%f;G$gMNyP* z$op_1P@I<vziqeMoBI3vxr)i-@eme2ku?|$^0ngB&p7vIP$tb4Y&u*0+@P|0ugZ7V z4oPWiYl||O47Ihj5z9R}IZ4aQ%LRZX0rp%1*krT>wY<o97?v1tW@e@l@xFYA?;`r) z4_O2TJ2*Iafio<~#mn#`sPSrgdO9N8`mYbZcGi_)wqTG)GXsAsSr`n=*NrRnNcth> z85^jsdZoNeeSLlWGxY%9-{qo;bk!suUy&o^zL9O+3h+#M+3@83lxyK6Sy|r8bIbcX n#n0M(H6||qg8p})KLi*6?uKAu1O-kZ00000NkvXXu0mjf7<`o{ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_d.png new file mode 100644 index 0000000000000000000000000000000000000000..8071e25b5b3f21cffcb6ad0b12828d41461206e2 GIT binary patch literal 1051 zcmV+$1mydPP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#y-7qtRCwCFmTgE>Ss2IvbM9ODGBc7* znz~)79X5PHqlh%Ec4Lt?q6zx4AYmW+vQZh?mp<qR`w#>L(HILw1%<G(x)fB%roFJ* zt5#IomUXkRX`D>Q9LJeEcRJ_fK!r4OZKvmQxc4%5&hI?W`Jd-`EN-{E1VAetDw1Fp ziv^-6&>=yVBu$cKm&kw)heItWC_s98I{zqH=J9ycmoHwlGAIYCstQF>k_yev&W6|P zRSA1x9wr+Y3$-lEAhDNZ17k+_^#k-<nkb6qq2b<M4As;?*R<({m=JSNV|W-omy18Y zE-l4yeLbSlsA;e%=;*<NNZY<0_M=CU`REZySs6m15F&cS6kId|wKX(gbbJ(>o2v0k z$vTuTs(?s=T!<ubF^XnGQ+KKsecnFwyu5`6o_koVE=A?Sm0Tc63Ge<h`yks1^1hpk zk-%G&FJFqVFadY_AzXjbop6YW8Q{O3huFEc4oj$a{&%BrAO}DGuo~4XHlg!D2LizW z48!<;pm(14;<k1Jjlb?ijyw<ZGn@#9f(QpAkSq#jrDP%}BL{u2pCA&6#04h=&9Y}9 z(>5FFGpw-CzvMgeF*flI{ev2UMgZe<%$}7A&HIc)qek@Wp>u3m@CW^P6CT7!cnCMC z*Zpt$ar*Kp{L$5n?x$A~N*%*v&wmtXz_<D*gnD|uUcCc@1Lzv+z~#TM;Y`<GP-F#R zdTl$l72jt$v0#o9PI@068-Y?$fk0augyLd;j0hpqSFiiz?`ZBkjk2P0Txsk^OKltO z9=?xdWlM1G&RP6<vjyYf03-^XxO54+u73#kXR3}1_0V+3OgD7gdUzYo!hGzm-UVxl zjUJm3oM=CewsU8(v#Jj5yE|d+=!SpG7Elrw6a#-Qtc1eLR=TO82Bq^Bp;zlcRqYzs zQmya@{P?+`3jgf64(m)CPv)VfBS>v(!i-g`U_>TiQImU0>~_4}QH%9Oh1gj68;lKe zdA@y4mCF{Bn~}>3GT%2*5Rh`=JOp}Yzp;x<ks9MdGY%X;;NnGu=pI?}@*w5r^6Ec7 zdjSs{J05Iakrro(4xSFFXL>}RY|3c;6h7TWk*(@WCY6*Rt+0rCHvz5(IuV;Zrbq^6 zlM6)Ple1^$rDzeSR4#`~2_$M(L6|2@iOkqS*@yvREzifj(3hMMX&)LtR}%8csz2_j zrqf#yMV2+%7RId*vDLFNRmc)C#-XN(vZA<Ny&7os`FyH8DFhyx<aCFuv2wi^U;xK< VW}6>0K+*sJ002ovPDHLkV1hvr;(q`D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_h.png new file mode 100644 index 0000000000000000000000000000000000000000..a0a4cd3bcba8e11e079c633c9c7f0fe0e3df31dd GIT binary patch literal 1173 zcmV;G1Zw+<P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$H%UZ6RCwCNR$ok8WfcFu%k6DPSGc#e z|7Zb0>J}T^%Z76ui4PjK3`L)4cvv!h*n?Z#Ml>jKB#XwxO<$NHd)R}C$`ZG@EYrB? z2pJ0mx_^dEak1Ef^tRB#_0rP6_tx($y$wvDcMsK*ob<b2?m55nJO6sIZ(_oR5b_db zQTat6dV$X8^WM7pde&?<mmP6oVS$|q2E7ykWlbg%BEMzV0uD>dvW!46#${&;z<~9? zfKtjqjfQUAu#Fr&+7Y{dpH*w~xB?yp2T77jYJ%r=_fDVg1f$UtCl34i`d-TAa;j`s z>l3B-*K_A~TlelA-+tuCxEKok9KChRE{Y<p)oNvpMkA{!PqC&_foUnD=ANG6s1Rkk zdcALWnmrF+-ucQ*DwTrrfMA-Y^J+i=kIu`IDo~K_&yVek3Jb=m+28Db{tt`$>E^k1 zPkVr-jTyB}0gsX-bv5n%;(d?JY2j*X9oa-Ou4`}KkE%I?<FjwhwES@Ms#AS^Wp$18 z;XtoPO#a=}^X3Pr#l0J^tR%J8Ej83@t*_&E551=un;dJ3Eyb90I*rvd5&_-(r6J#S z8^0K8`n3BDvKt(@sm6se*$kaZq!CSNu({fT8ypSPy{S7K)HsvL;LSl*q}TD3BW~u* zsH(CGVML)u$1|9KG7p2lqhuz5R*2SE?a2R^p97|)Kmm`IOEglQtv)WUh$xnb;!3&_ zPdpe$LR_f2^wXtWBo>J^)-~p`+B6FA0aG@c^-rHXdC<|>`6IV$S9txAk;uqMrAKCV zZWfJAkD%+<hN%(X2**`(dS^|o>HOvME_N#$Ga8JG7S<9ANBJ#`(;1A7j&9fQ*bxDr z6h-CNjEq!R4r)fGf8Z>`M<S-S);8wjw?9F~DkGYS2GPKGXH8#U95l@f^Oohrvd`>t z&4@!ot%*e9W<^Cso-7YZ@m<`#KzE}Kp6c#C{OBx8)Ev)So1cG<Iri2&$e=eMF)pIP ztAps<%NNrv+g=zwb?~Dv^}kFx7q47-6`VtDlgT7SQjS-5?wa&&G|Vu6FS_^K4Tb~Z zP%vaWaOi;5Y_w<zD`aDB5Bj=J4YLL=4QWi@zP(f5*7kk1$MYw#qreA%GYnHq@tX9m zC*<waY=8g0{T9o-Zco>QY_-P8SX4}h{44}iYpz=cFi^4v<*mn$e+7G3VF&jLOGVZ! z{Po1t(Q$2ga<U;E2-IVvQPkS)B8eJ3TeoddmLyE}C<ey>1hv!Ybi}`ei9ZVJ3HRhz zUhTgKx)D>Y+a1O(*F2d&umop_IFgi(ggYX61pw{}LFARreVkB9riv4Y7P4J<F8-B4 ziXWAsa9;JS1oba}C7CMyMCt|XumZ4IkflTw(_&K<qTh(Y6M>SH2un*#ERphmX$u0B nKmgDSVV?IA{m;Je{}EsS#S%`HL~px900000NkvXXu0mjfExR|A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/object-fit-bounds_n.png new file mode 100644 index 0000000000000000000000000000000000000000..aaaed8e10140fa9ff28f9e22b9c4cbd4b0b92ae0 GIT binary patch literal 837 zcmV-L1G@Z)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!=Sf6CRCwC#md{HYK@`VlHXHL})#ju~ zFtqtag=$j*ZOI`NgrM|bq=J7yFU_Ge=F(%yIfar-D82M1rUxrFxim#fDpd=jNKXn9 zN->_K#2;z0*{t7z38a$7UC|aw2Oe){c6UDS*SuY-sw(-@Lm+=){D)rZ5Zm2!I-S4F z!{M+G>Io!1H#b+%1KQcy(eEjMHmJ9Jx;{2G7Kf&n9o{G;2@f9@i^c1eO64==CLS$a zaN6#io(``hNnMoEmw4x$EXzyPYV`+}WiEBC;T3gRNa+hGmne#U$PJ^>_yzEffXkN> z-p<a>r+&ZR)!*O$6H{9>8l@8x6Sq4$Iv(TRT|FTdv;ti&*GN1bCxJkKD2mc$x7!Vo zNW?HRGh@a5QM1`BY28oI0QB|t_Ku`dDbm;1N8N5Wd&hEgbVT7LbvPV!XlTeF2!flZ zOSlM6_W&i`-Q6sFg2&@wTH4syAOMrY!$a!zdRe}&Vcno*&oc`No9>lLC9=Q2Ppwuf zU0GRSP)8<{sqXIXZVe0!lmTRqdLi#|em|hPr{?G9*Ut@QqfjW2)zwv!&1S{5wKXTg z+yjq2`FwtKd3kvYk8DG>OeT{Jz)vtDjQJ)+ZFQ71S8pPbkN_MEg+e{k)6*oGOfn<L z<m4pt@IF8fpshGQOVIZq#@{s#;f&VT2$5JWmv8!fJ_!}eii;c^9FW=BS&~ktOYrVF zI@EA57`y{-Up4LBvaqy-ms$Fn^;;Crt?}{kL4@}Ta!ruyH-J5>)oLXcMvKMr==k_J zlFQ{5c~Oy8p0;g+u~=-eUaxOLzGAcd!oot4^;?Xq>|FrAH|4Dcsd7y@l&x3Tnw~Z| z1sTQ}4B||F!!?6|RoUiY6F5}nK$!zYzRnC#&eQNk{xzZ8@_JK6HlPX@l|Lt(u3Wth zzs@~q<VfKjs7?85wp?xHo!&xfK$WIV)fTXe`CZXt{e^wR|Ea&h69EPQ(2pg(f>vbK P00000NkvXXu0mjf`Mrj? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..3caa5f8f8258b91b0f75f7c4527e45d3813156ac GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBC?Qee z8c`CQpH@<ySd_{TkeHcQqUYxtqEKe6XKZNRT;2w>%iYt(F~q`u?c~#5haE)P)Wg)8 zrt2NKKA~!%nD^8sF1;8IyD9z?TuYP7D_e6~UoCs9#4lj5RZa8i(vnh}`WcEw-JVKp z`;OLI{Ir;CS!i=Un^SS#Ox<PI9$WNH2;vIg)!*bAzkab`?V0COmcE=nqvyDGN5;g| zBmQqYzx;T=S@P+k^G_#b%{`gnH|a^ei)OvhYnR{b>MRuvrU_~5EG7C}?}r7xeldmN z`>q3LVxrsElq@=2*3V%6p24^{w>fi4SD$q))15usXU=T(^$FF=D%a;M_$+5&e0`gC zr&aoOmMtgFOm)5ckC#!`=(GOBzt^h7w}Zq4SRF6u{B?UCUJh2#zw8@}`Q-ajmqT8} z+_`l)A>sFRsjTo%wn5i>r<dw|%dn`~)cJusq5AXLv?Wf@lQ!4h-#_0^eYHlHM&JDz fJIoR#_Wfbl;hI{)9Q7C&RSXQCu6{1-oD!M<h$+X= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..3612755424869d359c6c67165a8cfffe727e26d6 GIT binary patch literal 281 zcmV+!0p|XRP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUyyGcYrRCwCdlj{z`APj}qU7}CvbC~$w z#OLrKrGTrg0fP-Um;Lc1C)r`}4QK~MH0+^q*hzl`y4>z027pnlGaER!LEhQ7S&LX_ z_H;d;AkLplAZty0Jmj(rv8CkCItV`tms}12-`o)z^L2ck^|h2i_^4*Bo}*=RHwe{2 z+zN8mRI?V8ep>FgIh6VsTT6Xh$jIw`bw^2$xTM^1JZ5ShjkShdkOKGwwDy2VPtLrj f<{ka2mjD9*%Ux=9a7FTA00000NkvXXu0mjf!F+Yz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/pause_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d9a3985218ad368c8c1022c463280a5ccd9e8b41 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G(>z@qLn>}P3C`teP~a&|yOR3l-}etjwHeuMO&-q5^(?$? zdwk~?S2HPf$*H-j|CXEhNa)y%)s@$7DXxjio}X-VWX-`<4>wIbD92Evl&v^RVo&QT zriZV)PTZZm+>`C}{1+>v1q<d+Gg<U##dC?GqYYx8rk>k<>&hgzZ#j1~=M_xe)E~hr r#-gWe71gofPR-=wS^e{FX&zwkSZdAB_Ne9y(1i@1u6{1-oD!M<NuOc+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..3d26cabb7af03e9995e4b6c8fa618cd1c35f0009 GIT binary patch literal 657 zcmV;C0&e|@P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeRH-mSE0006cNkl<ZI8U9E zO-~b16ozL&2ct$!1t}j&um$2cWxhMpPlobU79^UOuyMyvAb~%?f(08*>;g@U?7DE} z&J_s>x`8hG0~#q}TLRjZVbjAo&2$(#1%*kT)xGoNJ@>tr&15nX!!TROUF1q1%d!l| zaZCr-@qt0n@9*2&@ay0JD&_LE*xlJVArTx_Dqut{7?lkON#QqvA-)TvwIl5RMEtqG z4|UP;(Gf^$)Xfh;)aOxh6KVUGuv99YiIi{xnvwPRmuG@9f}$I?uw}q99*L7kGsocq z{>XZA2@Tvu<D%%VBMQf+;4-vG>K%%1H^vo2O%6$Vjv}5*WF}{!HR-CTB^GhNAMy3{ zKIuovTPlX|h+^Y&&`dZ!^V!$wS8;Oy83+zPUkFLYo~T5eVmzY6<V$D}pLkK^Me{Xo z8b*d5`THz>RQp;NXfePNoMJqpvFTSpM6J0sTzqOvf+M`VBC4^ftWm2;P>nIfa*FYY ztn~b*Y@~SmECwlwAekQ({7E%aphYG?k51Ncmtu>~TC?v8wL-VLA0b&lkRr=Qe7b5R z(k;uV)oORnwdT5Mboof=L;6Vgu&YO*gKt%cXEc|H-q<bVKGKamw$Cg`1yB+0VjR)? z_4WE&bBXAoQYw<&LpqUOWPnDcgj7hQ97nXV@u^uvYb2v&^ekx12d;FKu$zjZf*esc rn_bQ2c25e00u+mTNb$<#^Z9S5RioNClF!xD00000NkvXXu0mjfKQ<-; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..ecdf5a3c04e7e4aadd4c8e9efaba10918c8ee2fb GIT binary patch literal 596 zcmV-a0;~OrP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeRH-mSE0005!Nkl<ZI8S3> zpf;%MS$4jrbFm@NTt*~Jx;0fDi~fUfMce#|vFX*KKogju29jo2S@WF#FkIR+`(aK+ ze>l(tRv=~~$-sizDgV)NZq>xiF=>_RK!aGI1`>8%R$0$~?6~_wYC&rm&;&N3yqlcY z@*f|L&#IpaG>8l4T~Ne;A_&8YG3nL+vEk^ns*k>5>BT?|5<o2E<QXng+B|zwW&1*K z<X{*SmQeg39s5UQt(BHjF$OAD24eY$q|)U4nkkP;8fN|nMhG}^FbwjK%>EC<ei50E z&F#EmfQr<BSk=hf$pPrKU74lb|8pw)|L0Xt`k!Aj85}tn26+W1{|8|gpQs+7q!QE+ zmB7fXmc*R;k16>r|I>=v|7VnR{s$Qdix>=pTzz8?m{_}OLBm23<dVRsocW+|i_fh4 zpOD?~KQX82e^PEUN~B;*51_Qd4aA^SEd#_L!<0P&63Qb}%0B`F9cyIZN?4$L1~L#7 z%|bvd4Kh$hUeyQ`@Yo^*pFyBJ#sS3qKr9Z#vd}P70Yv~PB9J2nmq8$Ln5#gB@d2?2 z5K95EJjkmc7lI-L7CHC~f*A&K87NzTyeWX>I*<#+K@kIrAVLPg<YBI32MX{4u`m#W iA_EkDAPYd@2?hWqUIE$7%U3u60000<MNUMnLSTY%+V`>m literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/pingpong_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..60a9c1548dad12e507545e265493bcf63424a14c GIT binary patch literal 459 zcmV;+0W|)JP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeRH-mSE0004ANkl<ZI8UvU zO-jQ+7>4V=s8Ok!iXg!>iTPcK7w{Ht+<62+FW}OR3)h~&l{;6xfoCXQz@ET9Z<)ZL zle7T?FA15MXWs97XL`N=eyXbaEX(o&?vkbM+W`8JfIWv{cm!d^0K36zka>@@EW3uV zhL$@4e}QUu?{OTT16nn}`R-gG^Y6PTihBs_rrdT|3uxvJ*b9WMItT@$fc{WhK$9d% z?pSb)`U*mU)bket(eO9V^Ddb@L_riqw*bcWrmY}${0f5LhDiyEec!*z^Zdi*x)Fqn zCkD?9B%-WGz>}uZU7!*5=L`(cshp%~`Z_NJSA;q%N~Mo<vgK+>Umk!h?#P^x4c^;m z2c4+Ff~nzZ{Jb9NPU$L0_Wjzk$_)9A6o7(gO0DU(Z?bq79op_xvI1#5M~<e+ovwxY zwr{yp5Ca7RY~*UT>z~{z1oh5C2POywq75`Qd!`6QrkHF{fSLdR002ovPDHLkV1hkf B$shm# literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..ef0e4500016b41d3e58a4daa66622207ac170fae GIT binary patch literal 527 zcmV+q0`UEbP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeRH-mSE0004^Nkl<ZI8U9_ zyH3JT7zglHu!wkzNQfF^6pfL9ZLuwtn}9bQNHj5F@hwbz1_u`h8PG*G1}9&@!~|V{ zfd?R3MZn-?GAtaw(}*FpVCzYK6JP)RpY{|onM^VSLEL}=plgvNNl+9;m?+aZ0YPtf z*(`c4mr<=+{g-?$*T6yOrB*|*bT?|%e|mgGa?oqNj&$uwF<hxs{tP1F8kvjcSU1BL z)NWXXN7Ahk4lp?8oi;ZkoGh_kgmnn|R13lZJ)qY`^9g3zcMnjD)<!tMxajgMuzcXk zBLtC>mLLNRQ7j)i<iyB3Kx(uEnYoqdso>v0-Dn9Kh9Nu61rNkf>_ZG~BQ;urMqy|Y z%vf#X9wD%G>5s&bl9sn7Hv&^&7R)(maX+xOBL&xYQHwID*=$;r=co^chQJt@0Mo#M z8~VcW<JHYQglXBD{MJA}7-?&V8`@cK)ESN?t~&;urBdzR8a|9hU;=}{3TyyBKYRoH z+pi^WEuFqlezZR)8?G<|{I6j74#%z4gEE=SQK67;lu9KeNe@8MtynDHegS^$gGT<C RDvkgE002ovPDHLkV1k<1)zJU| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..73837b5d54bf43bef1427b8df7ef660df939f5a2 GIT binary patch literal 479 zcmV<50U-W~P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7<5HgbW?9;ba!ELWdKlNX>N2bPDNB8b~7$BF*k#Eh5!HoIY~r8 zR5(v#U>MPW5$Jm`uIXHCSl6@cJUO8OGJrWYy;`)QZT`fnjz#}Lm=uFR1~36JYfeRf zcxlt@hh@!k{=+b_27wG<iAk$e&#jucxuAB+e{@X9Ah0)pm@T!SwJfWw`$JY)&wp$f zpFto)Sb&%_KC6Cea$d`Sd>EHOFqd!u74QSGxNlf`adcYM$C&i$|JX1-gFvp~0%9Q` zmIh))8F^JB|A?%$VF|_m(J|330xFaRVkIC}F}L%I@r%fO>>ru^ABKquBarv_fLH{G zrGQu-h?PMubn%Jm@d{4<55h#p8q}Ktpzwma4&*{{6Ki)ZSKrtJgbaeo!(xyfD8LKE z!aytv4L?vgg2Iz%08iOK9h^YS1H{}w3^E*))<_1hq`?Z*3Bn*lKw@MAcnN^Z1OQ|< Vg7?Oz>N5ZU002ovPDHLkV1kEaxA_17 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/playbkwd_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6e5f277bc89f70eeb18cbb67d2fdac516c27fdbc GIT binary patch literal 433 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBC?Qee z8c`CQpH@<ySd_{TkeHcQqUYxtqEKe6XKZNRT;2w>i`&!1F~q|E=@jp*LjfXfwfkpx z@w0TdEc5l6m9k)V;Uhhk@P|hlPZvC4+ts|zaaUs*>$l%WrMW^Uy}rVI|H+}BpXL|y zYl^)4CGzs0i_G!MDxP-vGuv2F&wp<7F!Sg;<fh`txhhfOoTi;bXRk+7*Zbf3OK$uZ zed1e^#heu=vR32QK9!SRDxR#fJhokDiR|>5#J0#?g4J=uk{8F#CVF0Wy40**Q6`gq zY(cr1@vrF|7g=m7N@L3pUfBO>-z?9i-((~XNW@eGI$c`3IW6UzcCD%2m9+5BR?CAA z33mVNd!zi`X;w)om)rvW!=L|rVceVdZgQTPsP790X_f2iBIPgVP4!aje3xhUQvSiL ZhvJ3XLevj_a|Fc!gQu&X%Q~loCIF^lskHzA literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..835e3f6fa114936c8a3ce54e28766f911fc0e46c GIT binary patch literal 590 zcmV-U0<ryxP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7<5HgbW?9;ba!ELWdKlNX>N2bPDNB8b~7$BF*k#Eh5!Hos7XXY zR5(waluJtjQ545#dW^k`Orj#9EF#gyF<<#g?Y$5R0ylk&pwG~vO-L@RP3~N@>I(#c zwNV#-04dGXu*WJ~oz59%Kt^Z6F8r9yJ-_=u_c3fd9=9?K^MDK?9SxRc8II!^9jCiy zQ1tnfPQzQd47F<YUt}|x28rOkRs$RzCz9j8mP#OsKI(Pos3*nbN~NMMvREBCGjA8L zhHawldR5#K)kLd;3kZwVd0|{K8PL!z30q_+`cV~G*F4a~vzd29(a?)@DTSJeY)(HY z5gfj|6|-#_MLm*GOs6TbyF;MDm*5DTVm&9ft0{7Nw?Pa0diWAW{gh5q<PPjZd-lG0 zyZ;0QlgKF2rz!G=BA~?S+r8BrrfgU|kIW$B$bjal`L_;1#<zJ;H1Y0ZELubskXboV zEEdyiCfe9J0>TrFoJ$Fe_?<(hkqKmk+WK>kf_u?>!4)=1D^mhF#Td2Ia%xwJgxXu9 zyFvxD4|KEj$S|U#6bL;Xafq!YlQ(K2dJ<GLh|r&bj#~X@r&6i2TrS%v6beu*z97Yp c$>;NrzhSb1DTwJgAOHXW07*qoM6N<$f==k~e*gdg literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..87543b362208de1036963cb2f0db9c8b56bbfc50 GIT binary patch literal 492 zcmV<I0Tcd-P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzj!8s8RCwBA{Qv(y12q5>1C;>P3}V41 zSl_$+{J+0{lUmASFTlj=dY1hs*+qZ;{4x0V|Nps)w)qoc)2l^5>Y7TT7=hvpxG*EO zz%Fl@3l{{+Kl=XdOWBl;!gWCIUyx8)^PK-gx#-ugA8`Ep_fI(%R@N1{RTJ06q*bZ{ zrJ1{GQ<#v|fG}1UWtaEj2#^0CfB*W`HhV(#1gPu&WtH{($LXS<KYrlAKl%Ut``;O# zRo@R(#s+fTw5}3HkQ`PQ#b(rCbrDeQr%xZ=Hf>%ta|w|917s%<{|31XYxMv6`2&k9 zzpp!ce0S@UhqvEDlMOS}M~pbr(N83U{{R2?<mI!6l_w8vI{~s8lscgJFO>fur$N7e z{eZgcd+(kt%ceqY{sZ;KFC+{S`{xy${2zxwzYe~C_bThm(VcIhfyw~Y_!G$hu!~%M zWB&uyV-IXbMn)E>0UXe<_=n_*zp#W!Ft0%s{e`-K5z799WWax-GA&Y|{zKyb!)5?6 iF8YUL5W&(9Aix0m#sgc43D<Q10000<MNUMnLSTX$=Il8D literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/playfwd_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..7fadf843dde21e037de8231e0c529961dd2a887a GIT binary patch literal 401 zcmV;C0dD?@P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzGf6~2RCwBA{Qv(y12q5>1C;>P3}OIz z4IS(1>Ymrp(J=%{Ga_NKTx7t=$aoHD*aRgdB@w7PMzXyIG%SysoBNEWre+9`%Yx59 zV!}v{g@t7W&~@unfdI&72I)hOZK4Ak=sF(`4vv%B+S)}R*8%y=$gU$b*7%v2m^y)m z^#j>#P}hMCV<FL?Pd|VDY`S&p))J^e|DpIF(FXnb^Jm@Dr%ziyefsns>Hucsz-J*M zojv*d`E%vNhYwGHYzCPL#ebpv{{#&Jx}x{`_3KliHiI<(2I5~x_z%o5Tm~Kd`t@tp zy?giGLc@Xqs_`e10VpnF#FqPkQOg1~fCCy9|Bzhq7m86d;&Rb{sN$c{!2FBkivQ?_ v;4|nSlK)}+f7lEl#zp^-3?f+i0R$KT7f^r&QbV%300000NkvXXu0mjf&6u(c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_d.png new file mode 100644 index 0000000000000000000000000000000000000000..dedef925ec8be23ba8a6cf5ae7dbca1155d27f40 GIT binary patch literal 331 zcmV-R0kr;!P)<h;3K|Lk000e1NJLTq000O8000dL1^@s6l)^7K0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy?MXyIRCwBA^e>o}z`(#T3y4`6pr9k& zkrBpb`S+i3{=KIn44-}pGcdAnG3d*lXFvulzkf3@d}k10;1X44U}9up`28DA!QX!j z4D1|S3>@q%U;z*x)r4OR44fSQ8Q9q&0>3|^Dfsi3fq{bqh&drzf4xIh!1U`EBLh3< zf1n}=2Jul903FG}_Md^9lL3rDd=x8w{{9bkJqH^^0K`W#fie8Xg>&zI{`d}dERc_? z;P-FFH9(wxV$c4U@7{a?h6Abs#_QLwgBi{_hh2cwY9JQaJH-|e)G!86a1W3Yv`;^T d;s}5M0|4}5Rf5lS1ZDsL002ovPDHLkV1mp2gB1V( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c443af28088175c92cf407025aa727ca8aeeb2aa GIT binary patch literal 331 zcmV-R0kr;!P)<h;3K|Lk000e1NJLTq000O8000dL1^@s6l)^7K0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy?MXyIRCwBA^e>o}z`(#T3y4`6pr9k& zkrBpb`S+i3zMQrd0|%D?Sm4&_Z4AhO<@av}1}0VkhTne}8UFwO&j8}1DERx20W8P} zG!!V$0OF&X@QZ=r`wvE-0!FX`5FbUspT7(YKS6>ZX8{#}_$Uf~{bFSJ{*wW22Z)cV z0BHY@A52gam>7PcIg;t;@Ba*+KfGo5_7h?fh>xP+*Kfw~mv@i9`||D$!%v_BARkr1 z@867TfH?i_gA*@5y?q7rGMXE9PO<~}e2sI?;SV35pIHrbi~x!P7-R1g+dV)^&_4YT diX#953;@GCYqYNBpj7|>002ovPDHLkV1i7Bi17db literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-in_n.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5091875336aca7f00149a4d47c6d853dd491e9 GIT binary patch literal 315 zcmV-B0mS}^P)<h;3K|Lk000e1NJLTq000O8000dL1^@s6l)^7K0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy-AP12RCwBCP)iO0K@fc{BN4F}$FO4I z1mX@R5s96|0o=g>Y-~hCEN}|!*o&P+B1o{CG1IM7w@+qTB`@8t>h-JY5|d85382qN z;}c%DC%$(FPc^6%3&`iPCNW=a5Dy0=;bt5_MIa(*o)D0dAQb{a7=h+VhiAmE10#TT z&^+P52~rP3NW&I@?J4qq0?n-%SRj2<CEdjmq)3LiTyMsOLWZ;iY*|}u_t*3N&c)!b zMT)1MF<Q&r;dEpt`xnA2hhtPPSNWJS+{SqQTlIT6F$}#D#raEs0RY8nifVjJo0|Xt N002ovPDHLkV1i^Hdk+8r literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_d.png new file mode 100644 index 0000000000000000000000000000000000000000..540f5a9b1d9f6777202ff3b9e95487afa00eb70b GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^93VCa8<3oGNZ$fTNtU=qlmzFem6RtIr7}3C<R_&n zc;+Uirv{}arc@T5Otk?jI^pT!7*cU7>CgZF_RKtMG|syfNJu!m@e*T8`S4!9`Ni{Y zo=1D$@aU|G=@G9xU23pU<H!GY$ENwS9UW)%JGS++o^dyP`2W5=PtThio}M*t3hZWy z?w6F9(J;wJroe6$rvsy#SlI$jnSg)pY$4`<{<2O>NqNeWEFgcD*MU_>fYrcmRg*o3 za6%1_w!{p_zLd0zPk$sk>?IG2`}FkitS-;{&G%$0Qz5_bD(O078T*awF6In{{%00F pw!gUN=XAxqpOtPYh7AnN41AY^mwwy)bUM&a44$rjF6*2UngGzZZ7To( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2feb3bfb65d016a14120c61c12e2dc488288ea6e GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^93VCa8<3oGNZ$fTNtU=qlmzFem6RtIr7}3C<R_&n zc;+Uirv{}arc@T5Otk?jI_~M>7*cU7rQeY6kO7ZteM9SKgCd@=Bdit-Ny&~$FOKm> zJYYM(88Jb^At@onv4>-#nQzvckSS_gg^u2@{IyL~@S*tZlRxwsPA-?dvbaFvOSRqK z6t~>|=dK(Ro+dAt;<P>X6@$X@2IB;#OHn0Pl-OflF`md-<IJu%;q)p_O|}Eyro847 zkUe{#;p)HXAuF95%oT#IL|7~D<|GDZBo|iibJ3TM&iH%m-!FmLmiL#4o|}+dzVo#8 p<Jmmn?K)AH6Svr{ta__`fFb@!txtkng)GoZ44$rjF6*2UngD5{adrRz literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/seq_bar-out_n.png new file mode 100644 index 0000000000000000000000000000000000000000..cac5d5b54b2e18296571b841d0aea161c9ff7988 GIT binary patch literal 293 zcmV+=0owkFP)<h;3K|Lk000e1NJLTq000O8000dL1^@s6l)^7K0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy$4Nv%RCwB4kUI{-Fc3s%BN{Hk4QO)! zP6Qnd5(O1hISK_t+<-1!5GP0h$iF0JZS%>-lI4Bt@r-S%stVt+EJ~062=!_*XrT9m z*?bY})+ws5<yHus11wJ>;^WiELeTX|#QX3*v2?J2(v%#0Vj*Zdz;F|kt{7MdtR*Oe zUq)al;C!vKk3$w&2#oX5!3PKQkw*>$V?wrqZtB2?(LWHLFOA3jq!>4xwadgSdO~wM rhjq<!ciiVjK9e&qMNY%sTYv!okBDp9j(+d&00000NkvXXu0mjfJr;9~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6f94d00d4cf4426e907916b8d3123ed1fa090185 GIT binary patch literal 1368 zcmV-e1*iInP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$`bk7VRCwCFR#{A3RT%!}-nn;JW@Rf( zOBt2`vKT-pFQrA?3N==g(#8;tF}76`O^m)sOnlMk1F7i)X;N(?4@OBN?Sm!-Dovpc zRmxI8C{Um<3(LT;RVd8jf6hP)EmOus{F9TL%)N8I^L^+0{&N(Sm6aI)YG|0S4W&>h zAc`WCN+o1T9+qUOSjhkbRkA5QJ^==U0fHc4yJ~uR%4DB*)G$yEn3kRnjaJK7v|Z(N zI#FL&XX3ON!|euS#pahJ2^^G3y4`?lC96~r@IRMC<o-3i+kHbAHIHL*%7MWlGt|LB zNJ)-EXs8b1p$2;D5fl{Y+s#Z>UoS^SEKrdYcSmiQv^!w6O~NrV&)=zo0}-_&493WC z++SWsK!A*~F+2JPMxdbg8ci_V9uHKqjGXLL=(L)Rd%GFn@UR&lmRCSSD~<>YK}K34 zVq<n9J5vW!j1g*@)Z_M`>3S<{1eTqZ%JbO$*8sX`431c_xVVIH`fut}anPuPH$-t$ zP?KpFVvLbkTw221`~sR=I*}9~gM$4z2n-B>%jxodNNnsR^QHQ0uvjLrLci;E8WiN^ zpri9Pm)T&@QB+*iAUwQD_ylDYMjzi>l=0E=qqus#4b3;Z@a^dzp;fDqlbJ%`DztX= z!ZAC~<$IdcNQjT7JvG4XcB8xJ4njk8aJgNyHU_u_Qw|ztfVp!VI=u!(2lnFS!hIOH zGlGW37F=%p16k?GT=IB9fun~H&>p&JKNTF%>llh6Nv>EO>&s_kVs{&1vrlukiEJoE zBqzn;_&X(#WeLG#rkLnR>ZeN1W(`bO{g#<et5XxO@EEX9P_;UUKSxJwtZPFlDmj=; zj*KLOc(>HeUCK%n#MQ<w@+>l{daZiXxqX7}+xrZEvZye_c=yFSp@LS=q??&p;EWg> z&QKIKJun`X_leCxrIImp*MdDcX&jVEXg1qmwoRa;r~d(yk3Xy6?#@V0ME?Fgyn(RN z`e6D*(47%s_<Ly?R_g@XJNwW^V}ka1bli@FXd{ljRg55Vaoa6=cdHjw)%~cxSkL`1 zzi<!X5up#`<qzl-<!^EEK7Ogaik9|n7<5{sB*voba3Sih+#qS^5Er`(Bs>lpBXF=V z4`ZajcU2eB_-6-KrMC7O3ZBg+Gb*-j4y;(-GSidLH)=t@WenwqUO+*9E`O(Q?WO~H zmIJbw2(;JT-2<rl=~oO|tVoG7p{%qJ@swNUY|$<HFYo)ujv${5_2tR4_~O(#G~H;& ziML<ha1C)d=J5Rw7tqqxi>UBWe0rh`IphH*tBcsOxUKnNjqR4FOj1%T&Yb!jr_WU5 z!sRBk_Y5E_C6T-M97%blxs^xi&5|M%A9@bX6(?LSCtnnExKb(c82Kgddb_jJ{=IjP zp!k&nd{uE4waqsXK$+E2Z+$5*7bUMgkB|^Omg&UtuqMHC{V=zW#z?uoCvF;KVKsv^ zj8Qvq^6O9V^Z83S|62pTDgOYw$*BSv#pzt}e$RlYk+6B!8vNte7SX?RoB>PJ@&uaf zjnX2NzV;$FiHo;CZ|h@oF=oEN9)Qw4)bCrvde&^>6=KKa6d4#TNE8IWuCPj#tQvOA zI85w%=<nssdyxZ|ko8_+)yZ3x&1&U$1Y#-|&(6))G&KBfk|dc<4T(#|&!+#rhG`W4 aBftQ8x3b91qt&ed0000<MNUMnLSTZCjEa*0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_h.png new file mode 100644 index 0000000000000000000000000000000000000000..d88297ec21fa518b26aac15a8ea77db2518ce27a GIT binary patch literal 1611 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 JPDHLkV1kJh8pi+t literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/show-wireframe_n.png new file mode 100644 index 0000000000000000000000000000000000000000..bab4eb25333cad8f60f1ce4a13d5134f46633e08 GIT binary patch literal 989 zcmV<310wv1P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#e@R3^RCwC#mPt=jQ51mR)Rw`hfCS=9 zL^o(arDYOGWNVE7z!iptD>trP=+2dKh&yA{B`cO_6e5(N3>uW6f@nm7!9c0DeUsnC zTk|Xw3P#y@lao8V`|fwnx#yhsRM&Ntf88`?3)+9^Uu{PELixe6jGL>fgS+X3>_Uzp zQRF2uk9<WIhX?wtwSaG?CRak`;YD;w4?y%q@^MsE)fuAxBjbRkX$ksL=H=qy@n9&* zSY;^IU!X){T}uHL)7_KWPa|O<Q>vy#{C@wZ-mYgt8TDjsT@{CyM#8=0%w6UO9NVrC z&O-8$8Qm}@r1I{XP&AtZJjQSvQVj4kGOFwPV}Lemy4c5>>N-vdum<4!7#l3jYr5VE zypj#E!QP(la=#;Gb*OAJ9IzctZ_wA-mepjLW<9R^g2mm6>&BSZ2&ITb0U9<8<2_0( ziQqg@Q(J-C=Os|}0@s4hqW$54p65c-r^u7q#-=^VWU?76&T0azC=)0&9_b5r$SiPw zWrO>($u&3IlzGQ1Wl}@oQVT1lxWD^=;<#?f=l4%zp((9NowK~(!R;^?<a`A<6I8fK z!MbAkf=B~X2E_+?r(T$yCgPpN4HXB1X>|D{+b)rJlDZcg3mn`htl7c1#1kaWYOY*u zEz_N1iLwM#xOglDCaMeR581ZU2yjm3<x#92VK~J7Mq~j|d_Lb&))mRq-eOTME-oa1 zDhQw;YkNf{LZOjBPR>onam%uf@hpVpwE$zRy+(G1ySt*P^jcw1`u9|nL0-;dc%4U; znx={bpG6|1OAq<jf@Q^6bxV3+`3_O8Ael(C0uS+?ZupF=<Y`#OgY7MYneOx(ok$A< zr78`duVbjcubt@q40r+GYZ53lwuAKz`xEi_CH6LuPLbTa+=~DQJ$ag)djUV-ux^UL zHRLV{|B!pSZJMUYLlHDqsNqz;nM}4JEzBn=CYOP|lIQBuLe-;SX7{E=x1UmXo_lMR zY0xQB2N%!r?m5SC9j@RGp6HY?!vis;dmXQP*Mq7OY+!W(<?sr3*33L6>MX8)#7Cf= z!V&Wn33rv8RW<_k!i5FC>bm+fz}x(Rzy%_7Hm$DO9?P6e-_vZNzgaEdo*+qqQc>z` z>LZ?^xt>F93D!KoOciNE#UUGbDqx&H`bXLTc+-FB>Q?;W{}5mRBTLg}Skc!d00000 LNkvXXu0mjfH2v6k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a9a358d4695136288bff8ffbfaeb27c58099d8eb GIT binary patch literal 856 zcmV-e1E>6nP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!`bk7VRCwCFR!vXaKoA}8de_aNNc@8! zs7et?sFc6Z25yM{8YE7f8t(jxB5tS>E!<lrq7{Nxszgro(n3EDH1SvGjpGnwlMf_! zqSYqx?7p2hGjHwe?CdNO(H@T~m0*tJkk*=9*ClIg%UHAK0<hxQ>h$ysO-@ddQi{s5 z%gZ0t@0VA502RRbg#~&zF(D@^%Yq=Fw+9DR2@4UH4G6>j#uyS%P+B%%YS!~e(SJ6> z!3_*=Yild>`{nvNJ(-)!`MufSrw@%r=D*F&&EjeXtHVyVv9ZAg5K<IH^1I#ca4NL4 zL29+tReD~lQOG_a`)+U7>H7L_H*Hy8N$2p23mK?Q?M|C~-{;Yh|2|I5X8htfroa#Q znV%ev{jm<dxq=!7iU^}PB3I+YZtwIlf5MO%U@*l301<GFE3<6O{y})d(8!?3q0r$d z0jyLW<XG@IGUFQZJSzu6p)4;giGLKuh5HVq7e#}OlyY)PwRrv|VIhoi(ICf&80*r= z$D$Wha(8v-#Ez#P6K^LJ&zv+1rBfpXOucsX_@Jn~=FA-7ydZ0tBc$&4)IQBFP!f+r z&(1$5>%fpT_luOeKqwmI5Ur3#nY2)w9v#0Y&w3K^pjH_RRH~S%LeU_H(<hA{V5j4g zwZuId%D{7_l?;8yUIPp1?DX`;k!eDeLa;G^oqsBkSf&uORyy-s70@~o=<D6xUaw+f zv<2D*Gx7&YJr;1x08gLS>#y#lb|ghYE`3%S%vp<jRg3vHh^z~W){Q8Gsq~H7r~UGI zFBt??cil1~99%F*hwrG-Z06+?2U26oAg(900K_}h-u_z#GeBSP4O3fL>D@9Ag)j_6 zvkO~B>%k05G_1Xec*i1bP^k1RPxs|8klY922SBVVQY${JJAw>)y}YH3e|xk3TLzK# z`{wrK_t2p-IH>$HjC60Ap}yQ>t*f6`@_JadeY?28)Z$3(+OOY#_KuDYtHxM<3pd@H i!6^f`#bfQC00RJ+|5TXtLblKV0000<MNUMnLSTYk;+NO} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..6958e6ab8143e9ae9588e39c8dc8095baeb2da42 GIT binary patch literal 1186 zcmV;T1YP@yP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$L`g(JRCwCFR!eUjMG)@S%<kgLq984@ zWC>#=0tb_W6^Vlb;a6DUK;#1w5*H*Nc-WXlQ9vRGP8^MVi-2E1gfTdmoPxMm;b2D! zQdo(QDB0cFnd$1Hs&~eo$1W!}Bei;Fx~jhFs;{bB{&xHJ^FoL<8f`KA=MjzTh5<XB zPW!n|XXc21_j>JKueW9#y7seY&IklMb5O>)|JVJtK@<W21U`k>IvR{epeURJA*7l; zC_2G-2AvZQ2wu68z|PLjcJX^<Wo37HdHHVD(2XB&ym{}>dv6r~t*@_lCxSW@jAtB! z(oUVL3thVO_U9I?bl@a^_Xh)Iv_WI^Nj5INb#b4(-?soVO|AOj`|ma%KYpT>R#=rC z13RGzn|2Dd!B3LJa?tOae*cMyqM;rR2AcovZNI-=lugrA#-ouMMMIw>mS;Vdt=eTg zzf+(gCxpXVE0ZLZK@d0|T5D8Faau$|K%Ny+3axlo+F=lQJ*BiGJw-awW&ilWY^R3f z4Azov8TQ<3=XSvY@k=5Zb&W8D;(8J%GEQRM2piC7gh=`r1_8{^&;P>mR!PbF0H<nW z(xQ+DzaD(H_~PQkowB!49P3u20bv-FvL1~_I*t=ffY53-OAd`jv5wPJH7GjGW&?^l zJw7_7og#QfO{~+*d!Aj!v|C?&w9P@`$iDjCCs#@6WG#Ov1<q*Y=js~cQOTgNK#ZEO zRr-gwzTW&)Q5@K<*EhcCimbrLIS29kH=A44bxsK}*YR8gB`Cpo1|08{Vn3yz&wp^G zOEMCuY^Y>(zRRqH%uAOyzHGIcmPy9zJZmZ!su^@px5{{9B28iMiJ*027)2V<$;HbX zSA9;S$P9-=gUzO;Fsg9`9PvrvGp&}Drfw0l20{FHzJ4GpoqQY}AHR^lbNdPd1I8HO z`RLngAJdMQ(Y!+{CFP3iEMK+f?QoqUHwQPSF?F5NI6ORjhi9g|B(iAAQ5b~4GkZ;} zwZu%zGv_Z~|7mY;i{-2K{P1lMPut+6C$))hfhx;UiP#h^%CiUzM~(b1*kHY^Z-}bi zxH)imaq_{IrXb^ZM77ui8LAVZm*4%c8$~0{1WQsYwJ|llgz7t_`Y!6_Crx&=xrv}R zZys#r?@LQdy9)~ocTdYnavQ}z?%cWX;GYK<%I!0i>=4h?@XKVvy!TF^_Uh_tHzseX zyBH=*5Ew__OjO^0DZWTu%AS4Y>^@QUXR<?O1->y;r&9DTNv~$xhZnOmZpy=<K{`de z5k<uwQV$MHK)qIA-mj_0QkYrBQVT-K4tU4L$u1l8MB?yH$tf!`J0Uwyx#;~V!*RtD ztN7e>?4!yqAh~SSHh&ZM+|-=1W8)5^Yayz)X-Owr0QK8KMHaK8?LR>O|9cXLmoLEm ze!o38H#c*@!C>Go+|1tVj~+f;qw&;k`2Pwp0Q>PSDUqFHUjP6A07*qoM6N<$f)~&; AN&o-= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/shownodes_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..599deaba4055fe3fc91a0905465b3120a46ac33d GIT binary patch literal 564 zcmV-40?Yl0P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz)=5M`RCwC#R?SMoKoHI*ZDR4_BPjSF z_D!T9Rxh5!gNXJho<zioAijw{h@dYaHc5Ba+3+o$uGutO{JC^sm?k^BGyBaqlM1bM z^w$zc{~)WNzbYNy!p-g2Wmy0E>hi_)J32nOAal;&k)PntukUZ|f?{(*CSizlNhPUb zJ|^f0J_#j}lg;2W=i3%u6L1ZhLX*Mp&_zMu0$n`YJxLI}EGeT?D6-df%>>iXDQtw_ z9p9yzg7#q*4#{g!FgGiq5;}sv7e(QlNZXrRwuT1M0ta)+*qWPA$`UGqh=j}w*h&V& z18q@#9N(?i5e5E&?+Xd4cK7yWBxpY&^7L?zR+ynf>&5r=Dv@%|F(9ZtC`Jvl8Fwn_ z(fS722?8~?O52)p(qym}vL4=<dVVrLiFeXB9J~ypCf{L?+LFRps_O@1^b`R~MmW9f z?0sg9Z)=n@7Ef@)32U32k}aB`Z8MSLh9+0cs+xU?@BB7nFm=ZnwTf79Fy}xRdjXa2 zD^szQd``)pd2T9~VQ+@P?abcFy0g~?vctcFdgq}N$=1u(Re_<}sbkCn#mrYNGaMwc zjX9(>Ey2Gs!to6sgNx|Tf=XYI)-_%I8~h3R5?}yq=ffk78PC=L0000<MNUMnLSTYB CoC1vi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e4c0fb52ac3a974f9d95ab1ba41cd82001c101d7 GIT binary patch literal 416 zcmV;R0bl-!P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzLP<nHRCwBA{Qv(y12q5>1C_uqHi%Kr z&>Wxq|4Zjj!Yu=;<p)v)K(R(B9~@H`&z)dk1Q}*vY=tZ8{$DtIj0Iw$g&h+U(_A21 z0Vu`_mH!7)0|FpLOiawoe{nfUMO{ajmrr00BO{|Pk}w;T{sYBegBY2ZSaAAHKv47+ zkm5%Z=7hNhh{28ko4~}(f-ju-v50X1@oy+*B-WshSPWx@x`+`a`k8?S;li(9Jk<t< z<t$_|WCKtP0=WnmzI*G^hm(hQ)O`N@F%%e@*PyP)h-HY^n3(aVB~XA~I&<g%kan`P z_bG#h&0k~#K~7|L@Ce5m{ReieWPw`7iliRo3ub5-{Q%-$K(GIWN&Lg%8fGL6i*80J zAD)hY2K{5f=3gZ64><)Rsr>^r@E-yFsQD2Iql-fV5m-0^1Q-D1k!JnM)a_*e0000< KMNUMnLSTZjF{jA@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..46fbd79fa12b2943f82e525b39a7461317d6c1a1 GIT binary patch literal 389 zcmV;00eb$4P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzCrLy>RCwBA{Qv(y12q5>1C_uKGN`(9 z@qb1n0OSh*aRU+`q=v9Te}4ah$uk$#Pj>|J&jWEih!4VT6>&^J%m`D@g3F*^zkV<T zMrRAlDyYx-_wTRouU~&*Qfx50p!h$2gMR*Ckd#)q1#|^JniMAx|AQLvm!OM&{`di+ z__0WF0P$}qChWDJKfi;hk5~+2g}R85s8|De?fRvY+Fw3@m<3|PFuDN*4f+YxboTh3 z4;z+Et$Ft3Ug-DlU$6ay7=$%X;q=<?UvN!-kL+2007yGIdq<T)!{#r#fsD8^Eilco zKrLd03Bl48G>kwt{Q~O$i`PX^8~;Jm3M{%Ap$0Lay9%E{|B&eaNSgjY4g5!}LI05q jLl0y|qQi((01#jR0q~AV$n@?}00000NkvXXu0mjf?>DA2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepback_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..f0c11400260469104999b424db4053082ede28ac GIT binary patch literal 348 zcmV-i0i*tjP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy{z*hZRCwBA{Qv(y12q5>1C_uKGDuHP z?>{3F0P+QZxB<o|)kPqAW*r?JM<9J3i0eUo5C)pXglr%SK2sDG6@|IEx#s}Y`yvUm zLFqqG{GXsfJUl$NfD}KPFeecIgBtLcSg-M85#s=2R;U*li8bgW7BLJ%2paV6-8=2S zfB()x6J{hi9X);e^ux7l*J{3g{Td3)O4p#mSo0JM0k44r^xnOD2Y|Gbrlw{Ykp2w~ zWE2A#ab;Ryl(RrBVny;26Eu84vGxOqe*yLXCFG+2Pz`^OqMH$F5Cgia@EP<EiT;nI u=?~Pvf5aN}AIUKEKxQO5j7S9l0R{jnVNb_V|1WR=0000<MNUMnLSTZ@z=l)+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..f0160a16a3e7ecded9159353a670599f18eb1e9c GIT binary patch literal 404 zcmV;F0c-w=P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzHc3Q5RCwBA{Qv(y12q5>1C_uqHi%Kr z&>WvUFfRT(UA}PYAczeT0IL6Y>HJB!1AzSh7tftwU}9ooX26G;nOXMf8(B_JR@D*( z>SG3pL45*ZFaZr^1PQP(F*6h6@?3sF;W#}bi*%@ZR*-Li_%BrcFAEbB3$ek#&&I~G z0?KBGdKHBKL0!Z`PS|n)@gFFL#M-H&yBR@-k%EDM4de*qSO+tJ0ZI@91Nq6v_itjE zAqFu+!w5AU5pu!bzkhpgU%%jU{qpJKP#6703L`LJv3Co>n`^&*`DA_O*sgofm|}oN z|4$^tAO<lq;g0?TJ65tn!-EqVJO2*uS|fxM?f>8@n?UR^Lj#o!mR^uF!_v`TVCw%% yDDMA3qZlR4AQ^;6M+6LlDMSfaENU140t^6SVP41ShK_*%0000<MNUMnLSTX*J)-Xb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..4513970b5f1e35aa5e7b24e542c83a56893a09ab GIT binary patch literal 384 zcmV-`0e}99P)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzB1uF+RCwBA{Qv(y12q5>1C_uKGN`(9 z@&C&91&)l2j7*GB01{(F0zf`tgZ}*f#qjU%-+d(wGbe<_7m5Ofm_cGtJ<K4wts;(* zph3TW{Q%=Xe}Csn$tm4RFYX8i@qvI9ikXr1v*0x7=MQ8qKQlAS3aA)65dVkbe*_Ks z`QryJDGrd&p_qZNK|jCal48RaEKK;k2E#wUe|z%a&ed3`*Fa&zNObi71Q`ItpFY0t zJ$7J=&yl?wj{k%h^cN|N2z%`}For&VdT)Jr_quz~SOSZI>_9RM4j6G|T3~u(frbYs z)I0y62K<Hj9u(j}{eSSf2x{YBs0NsqVA0J8W&bDWHK@Wr*wPHtAmY*yl0uAtMN@-6 ej7S9l0R{lR^peC?V+23|0000<MNUMnLSTY|zM~}o literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/stepfwd_btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..eb98eb4982c7c4f9a1390e01b7a14651a68bd4a0 GIT binary patch literal 341 zcmV-b0jmCqP)<h;3K|Lk000e1NJLTq000sI000sQ1^@s6R?d!B0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_en%SRCwBA{Qv(y12q5>1C_wwGl&5c zM%b{fuI_&w9UVugWsD#U5`(d!7_J_#ix?Rh_W=!?ps1)Q3gj{)`GOhA0^+;|G%S~g zhv$~2re-iyh!u*Nk@d3>83Oz)EG#RaY<7^3p!gppfz1JR5sF?GG7MtF<_IPtWA@3X zPoH9;TxR4zCfcAsfBy77e*D<y;lqc=p+bL=!iYGp{rK_2`u6SH_n=_{13!@rL&Pqw zObd*57HD{ILcQ}3YQSHpl3ySP1NHwQ<f8vjO}~)51dDD)DEmJ#UIQuogDuTK4MGtk n<f4Dr0v1gT{xBjH00bBSe7t7AxX*QK00000NkvXXu0mjf!w8AW literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_d.png b/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c357d68894881b08d05ee14bd6cc7297fb054707 GIT binary patch literal 688 zcmV;h0#E&kP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!Qb|NXRCwB~mCb7tQ53~bl1V0Q8We<- zKo?U%T8Kix7F-B^6vRzyvk)o>ZT|ojmsPNF;liDC5$K}mMsy*>cHN@xtP2sber(#b z1gA{I6tSYzINswUv^F!7Oqd6cWZv9&e{<hG_r6e}P{;x($}|wr2!}$@qEW<RG3a`% zr0emVMo21^GBTMAlF1}OVie@4R4T^irdcHDAH*;Wq3eNz(&;p+)v6KZ10(STxp}5# z5Pk9p!}SMWk-U5EE2>B&qCl==?G3Q>pmF`^>BcqM(cVoUo}g9PI5P>neER?Too9`Z zx$B;;G|#s~6F}l1a7-fD-U1fx0n791%Nf<#ab;tb6udXvCWzz2sDe-coi=dsGB9vg zfD6ErDPZLVaP@vuH(E>CWF#HGfc&iN=m-8(fWebMpAOuRwWU)kGOr?T3B=<%FOKbP zfmrhUO%{I&T$FmQ7kD+_yj!@|$pvxpD8H+|zD}UF3hca<`9c1d1^NZ(k@w%XlpXE& z?rVB<`&GpxzkdxJ|1F=^B@yi?NNof7Xad~>%I;EL&9-wid`&vy72t@7A(4=Unt&vV z_0PcKS2=<4fW*K=Q(e;!fsq5iApw5+AtI1GjoflBVBa7XE9;07rn;DT=A_$Dkq}bv z9v;f`2GuLRUW|0)=8PSL#m&0&E$_ow=ZfF2D#E9VD)P4KhybUy^7yuM2Vq2Rpr~BA zzQ@?K|La{YrIKlyux-2bT`slaG%U;FPf3f%^<24JF7j^YO~|QfT98+|w07kF3NQen WovqTR|M@Ne0000<MNUMnLSTXh^h&@0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_h.png b/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_h.png new file mode 100644 index 0000000000000000000000000000000000000000..813b0a7b6fa81cf87831482381124353d9da6709 GIT binary patch literal 1128 zcmV-u1eg1XP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$3Q0skRCwC7R$FfyRS=%NczsElIJR?f z0@z7wJEToSTR?=UtyGGTs#HZh8Kge&1n<C4K)mq4k3gabRDrONPzs8QL<3Hv5<%5E z#%Thv>%?{(YR7wDW;T0@vn0hY9cg^LXXnf}mv4@i%Vx7f0FXuv!jq9Y>Q&J+&2%gl z3q~T5CxTL`RDx@3YiZuHtY9b<0$e-N<xk$5?bMdk@g1a$fRy97jv{Pr6^F^bQz0<2 z=jq2VjQ?ou?o#B>xzAJXeP1M6)O8(HRpn@<J+dSFgZz-%K!?3-tmnezAHO;G;HPin z?fc1hKg=kK!td_x3bbx~|3zwR;k)U>0%j^Q19$2A&s@I8Nq*%>>U61DE&D1rzB<uZ zzZ=v4u7{=YP{}_wnMa$Pvb}ZWsn_4h1mY7F%97`KtKAnX-Jw7v!PDpO%k_%yug^Z7 z_4f6KMqZtTz-SD%?<_{EKQBZ#9^4;{zxDn+Iiu{bl$;<4mdE2UnZHLsRA<tIfG_}V zbN<>1u_q9UrQZgB*9d6p4s<6{z=cI9Uz<x7fB3Q#p1G8BfIyNY+gCdf?eivP!@%5% zky!FZKGrii4yGZ4rtE;BxA4CTAOd(31JGRkeN=9>Y!G3uY^LGzNf@HxDqZo@k7l*) z&EY`u)KdRzR~C)NPM8l%Ah$L_*O~~ffvVInvxflP0J<W38_lMmEPOpBuiQSx&b9Z5 zFhqvVfCkbZWSEhia#v5av0ez9aRUU=hd>ym)&ND`0;OC7QwlXv_^y{O-|jpA<}D%v zo!bXQ86DIkr>=BsfcMzR-<rSPJZJS<AozUv4lAHHL9J|qUTA<ZF<mfZnMV>J25*X@ zNM|`*sf-p2*k+v@2=_Q@E=A%wekN}f3&RiRRuW<|0G1b7R8_%TLme1j)&`H31@uAG zMj~*Urg03&VV`BP(z)r4nd~)nB^&dMO_ltwT)EM>b$wG=Ej$CdDp}s%rk+X`jG;u? zv+~Or4vz(sXLCdzqBmw{&l5d6?EM$WE$Z@oLc4QwTI}u5@X@1HVPfW<5<mF|m6S4e z_zxlmCFHX&LsxgpIzCe%=MG5wm>doSebK5k^F{^+M=LZ>bQnuv2+P->YmJ{>CKD=) zaqNY>=?}=_$as<6YwnrccLlkH8yc&yiQlUvX9MZQ+>|tRVTtWwUrZtS;;MnR8s2}{ z8zO}1>iJ_ba!5hVrHl~7vK>7A?2^~()fo`=gly4HAQCwH`?7j$MvN2=G3-bCx_|!S zA_YKB3EUAoE+PRvxXHyX)q1T~3!)qdMzhg=z?lm>Qxc*vvqOC}hYmQ@>vcL;C(>Lk ul}c&Ujz3O_SqMg@w3ze%`#AYWfB^s-{G1i-|5#-J0000<MNUMnLSTaZ_YztF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_n.png b/Templates/BaseGame/game/tools/shapeEditor/images/sun-btn_n.png new file mode 100644 index 0000000000000000000000000000000000000000..70152a8be696ea74f636da0b93f1fba3be75eb60 GIT binary patch literal 780 zcmV+n1M~ceP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!u1Q2eRCwCFRLyG|K@@-c5kG2*Ayx3A zD9RRuh~P;yHU~v$N$D}CLg^tl54jae^H=nc<kCY6y@x`fEmYBywFvd1_T)hnL{wD# z+TFfcX3KONKPn{yKOQ?fGr#%0_j|*sstUI;c(|=W6tP%05{bmcHXPe1iu~8q;msw) z@eRL92#)UM=-Okle%l(|N1WpUN9<%Smpi5VL=x#`&ekd7jCJ8Sj#YIunM}S<r&F-o z?I$=zNF_y4$lS+bv3QMZp5feAyg;2QR4M+b(Mlrn>2|yKd_LdnKp-HcQYrBJ{ZJ?r zAe+rXr_=e6&*#sXR0;?MA+uzgn1S<nJSSeSSIT5EkWQz;>2yLU6oO<j2{xPUQ9K@h zOo;>vZks+*1wlAGI5+^3zFw~f<M9|~vzeyc<#GW{r%2b!Mm72nm-Gp-@FnK`lGMa( zHbb-7gu!5-kzmT8-|y>bv18bY#bQtKQqP%vtBI(q*ioz3>yj+XV6|Fdu~=v%?RFcw z-7d`M^ER$AVcp(fyr4CLwM2St^gcm{y|2}3uP|yvgm5?<YO09BR;%@yF#^w&L}4Ac z%yoL7kj*j{{H4icdWgl>)}ZCJ6r!?s<#PEgJ#<EXF_$EB3ldk^TU#8BMjv2$O4Vw0 z91e${SS*%9{J)E;+Nk7HqtW;g3<gs?&nuNm<(d+yMnx<8i0SxKtlLkFW`xCPVM63^ zIL=0+(Th^4^yQ`+X&lR5Z~E3rOe%H|cOsEU2ID=7eZ(HBO{3NK+4pZ_BE}Q~B_TBv zk^CFRUBq34jVdMoO%#7MEjd?Q_cAbUB88RFeOFA;6{a~w#>L7%J*o^5M>}3&>?`W7 zGQ4%N;Q|{eQt3u8JS#i{{ZNIuh`MRR8p(`9F&w;X-S6>BfB^vYs#@5Yq`eLR0000< KMNUMnLSTaZ0c*nm literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/transition_slider.png b/Templates/BaseGame/game/tools/shapeEditor/images/transition_slider.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa51ab508ea4b40720c8ff931e095db686d87bc GIT binary patch literal 802 zcmV+-1Ks?IP)<h;3K|Lk000e1NJLTq000*N000~a1^@s6itQw000001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7<5HgbW?9;ba!ELWdKlNX>N2bPDNB8b~7$BF*k#Eh5!Hpd`Uz> zR7gwJmETKKVHn4!H%0{Qw*DUf1n;`gt4SAK#4wT}HeqvYP+J;V!~B_AH`av~IUHy~ z`{B|X1<@$&CcJT4%@X|dJZGGpv$LaYQBlH&^FEK?=X2h3_Kr0*wfw;IxBh<hW_8Wg z6>@pfy5{N%{S)$R=B-yq3n;pCbKS_*F{eJSN5w1o9INjN1{yNgja=<>>Qk@E`Lw%R zK|}Vs-nDa1y@veopjbge_PXA+bIy<#R`Thcj~_TXDj8_VTsLyHznp7-0^ULh@@ zXvkbQa&^q9&s%e1|DSX2-1=|bxyt?&m5nnz+)5&`F9c-Wa@G@(m~JKfaG!88AZypG z@dijHn}s%Fu|0A*7#JuqI2a@nIcL9thPgA{EVL24w?}FVB9BT$MuPO*t;v?<(P%rt zV39%r<eSRoCQMC1JU%49B2DHE4Ee30c5dD-vM9MVsWU}*Fb|=TA<0!`%kp?!LK%=V zUSUgil}I4+i&gT_1iTV){<}r41mOw&HLJ>&<>4@_ufxWMh1Sui$}d;R14(!-vNuN- zTKA5syeeCk$6~O$Dv!Blp|zX5_c1JfgR}@uwC<feMc&_Eel6Loh1S}y+4&^A_zEvY zXrguCG<jbiEG@zEvW3>#cgk;w{F%s}i`+zO>ASy{o*r0OfK<vtYwbJb*X09!@&%rX z&_rwLyUV+~VQvoQ<w%)mt$nBbn#jiwVM2r^T1($u-qj^R8JeB7n3)0XJLOl$<x92$ z!v_}kKZEq$<$iw$e*dw6jT1UMLp5^WSO>n*V|-)SIN?e><SN&5ERyrWC6zh2U_a+) gW#xZWSv}`J0mbW_G_1tPrvLx|07*qoM6N<$f>z3MFaQ7m literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/images/trigger_marker.png b/Templates/BaseGame/game/tools/shapeEditor/images/trigger_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7dab994f2d8f6eaff12947c8261fa454d84f46 GIT binary patch literal 136 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-T!3HEf_T+>BDb50q$YKTtzQZ8Qcszea3Q$n8 z#5JNMI6tkVJh3R1!7(L2DOJHUH!(dmC^a#qvhZZ84N#G&r;B5VM0oNU^En^qGqO1F d7__JHFxayjeJ&T2NCYZi@O1TaS?83{1OPYkBG&)_ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/shapeEditor/main.cs b/Templates/BaseGame/game/tools/shapeEditor/main.cs new file mode 100644 index 000000000..721313e95 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/main.cs @@ -0,0 +1,420 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Shape Editor +//------------------------------------------------------------------------------ + +function initializeShapeEditor() +{ + echo(" % - Initializing Shape Editor"); + + exec("./gui/Profiles.ed.cs"); + + exec("./gui/shapeEdPreviewWindow.ed.gui"); + exec("./gui/shapeEdAnimWindow.ed.gui"); + exec("./gui/shapeEdAdvancedWindow.ed.gui"); + exec("./gui/ShapeEditorToolbar.ed.gui"); + exec("./gui/shapeEdSelectWindow.ed.gui"); + exec("./gui/shapeEdPropWindow.ed.gui"); + + exec("./scripts/shapeEditor.ed.cs"); + exec("./scripts/shapeEditorHints.ed.cs"); + exec("./scripts/shapeEditorActions.ed.cs"); + + // Add windows to editor gui + ShapeEdPreviewGui.setVisible(false); + ShapeEdAnimWindow.setVisible(false); + + ShapeEditorToolbar.setVisible(false); + ShapeEdSelectWindow.setVisible(false); + ShapeEdPropWindow.setVisible(false); + + EditorGui.add(ShapeEdPreviewGui); + EditorGui.add(ShapeEdAnimWindow); + EditorGui.add(ShapeEdAdvancedWindow); + + EditorGui.add(ShapeEditorToolbar); + EditorGui.add(ShapeEdSelectWindow); + EditorGui.add(ShapeEdPropWindow); + + new ScriptObject(ShapeEditorPlugin) + { + superClass = "EditorPlugin"; + editorGui = ShapeEdShapeView; + }; + + %map = new ActionMap(); + %map.bindCmd( keyboard, "escape", "ToolsToolbarArray->WorldEditorInspectorPalette.performClick();", "" ); + %map.bindCmd( keyboard, "1", "ShapeEditorNoneModeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "2", "ShapeEditorMoveModeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "3", "ShapeEditorRotateModeBtn.performClick();", "" ); + //%map.bindCmd( keyboard, "4", "ShapeEditorScaleModeBtn.performClick();", "" ); // not needed for the shape editor + %map.bindCmd( keyboard, "n", "ShapeEditorToolbar->showNodes.performClick();", "" ); + %map.bindCmd( keyboard, "t", "ShapeEditorToolbar->ghostMode.performClick();", "" ); + %map.bindCmd( keyboard, "r", "ShapeEditorToolbar->wireframeMode.performClick();", "" ); + %map.bindCmd( keyboard, "f", "ShapeEditorToolbar->fitToShapeBtn.performClick();", "" ); + %map.bindCmd( keyboard, "g", "ShapeEditorToolbar->showGridBtn.performClick();", "" ); + %map.bindCmd( keyboard, "h", "ShapeEdSelectWindow->tabBook.selectPage( 2 );", "" ); // Load help tab + %map.bindCmd( keyboard, "l", "ShapeEdSelectWindow->tabBook.selectPage( 1 );", "" ); // load Library Tab + %map.bindCmd( keyboard, "j", "ShapeEdSelectWindow->tabBook.selectPage( 0 );", "" ); // load scene object Tab + %map.bindCmd( keyboard, "SPACE", "ShapeEdAnimWindow.togglePause();", "" ); + %map.bindCmd( keyboard, "i", "ShapeEdSequences.onEditSeqInOut(\"in\", ShapeEdSeqSlider.getValue());", "" ); + %map.bindCmd( keyboard, "o", "ShapeEdSequences.onEditSeqInOut(\"out\", ShapeEdSeqSlider.getValue());", "" ); + %map.bindCmd( keyboard, "shift -", "ShapeEdSeqSlider.setValue(ShapeEdAnimWindow-->seqIn.getText());", "" ); + %map.bindCmd( keyboard, "shift =", "ShapeEdSeqSlider.setValue(ShapeEdAnimWindow-->seqOut.getText());", "" ); + %map.bindCmd( keyboard, "=", "ShapeEdAnimWindow-->stepFwdBtn.performClick();", "" ); + %map.bindCmd( keyboard, "-", "ShapeEdAnimWindow-->stepBkwdBtn.performClick();", "" ); + + ShapeEditorPlugin.map = %map; + + ShapeEditorPlugin.initSettings(); +} + +function destroyShapeEditor() +{ +} + +function SetToggleButtonValue(%ctrl, %value) +{ + if ( %ctrl.getValue() != %value ) + %ctrl.performClick(); +} + +// Replace the command field in an Editor PopupMenu item (returns the original value) +function ShapeEditorPlugin::replaceMenuCmd(%this, %menuTitle, %id, %newCmd) +{ + %menu = EditorGui.findMenu( %menuTitle ); + %cmd = getField( %menu.item[%id], 2 ); + %menu.setItemCommand( %id, %newCmd ); + + return %cmd; +} + +function ShapeEditorPlugin::onWorldEditorStartup(%this) +{ + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu("Shape Editor", "", ShapeEditorPlugin); + + // Add ourselves to the ToolsToolbar + %tooltip = "Shape Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "ShapeEditorPlugin", "ShapeEditorPalette", expandFilename("tools/worldEditor/images/toolbar/shape-editor"), %tooltip ); + + // Add ourselves to the Editor Settings window + exec( "./gui/ShapeEditorSettingsTab.gui" ); + ESettingsWindow.addTabPage( EShapeEditorSettingsPage ); + + GuiWindowCtrl::attach(ShapeEdPropWindow, ShapeEdSelectWindow); + ShapeEdAnimWindow.resize( -1, 526, 593, 53 ); + + // Initialise gui + ShapeEdSeqNodeTabBook.selectPage(0); + ShapeEdAdvancedWindow-->tabBook.selectPage(0); + ShapeEdSelectWindow-->tabBook.selectPage(0); + ShapeEdSelectWindow.navigate(""); + + SetToggleButtonValue( ShapeEditorToolbar-->orbitNodeBtn, 0 ); + SetToggleButtonValue( ShapeEditorToolbar-->ghostMode, 0 ); + + // Initialise hints menu + ShapeEdHintMenu.clear(); + %count = ShapeHintGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %hint = ShapeHintGroup.getObject(%i); + ShapeEdHintMenu.add(%hint.objectType, %hint); + } +} + +function ShapeEditorPlugin::open(%this, %filename) +{ + if ( !%this.isActivated ) + { + // Activate the Shape Editor + EditorGui.setEditor( %this, true ); + + // Get editor settings (note the sun angle is not configured in the settings + // dialog, so apply the settings here instead of in readSettings) + %this.readSettings(); + ShapeEdShapeView.sunAngleX = EditorSettings.value("ShapeEditor/SunAngleX"); + ShapeEdShapeView.sunAngleZ = EditorSettings.value("ShapeEditor/SunAngleZ"); + EWorldEditor.forceLoadDAE = EditorSettings.value("forceLoadDAE"); + + $wasInWireFrameMode = $gfx::wireframe; + ShapeEditorToolbar-->wireframeMode.setStateOn($gfx::wireframe); + + if ( GlobalGizmoProfile.getFieldValue(alignment) $= "Object" ) + ShapeEdNodes-->objectTransform.setStateOn(1); + else + ShapeEdNodes-->worldTransform.setStateOn(1); + + // Initialise and show the shape editor + ShapeEdShapeTreeView.open(MissionGroup); + ShapeEdShapeTreeView.buildVisibleTree(true); + + ShapeEdPreviewGui.setVisible(true); + ShapeEdSelectWindow.setVisible(true); + ShapeEdPropWindow.setVisible(true); + ShapeEdAnimWindow.setVisible(true); + ShapeEdAdvancedWindow.setVisible(ShapeEditorToolbar-->showAdvanced.getValue()); + ShapeEditorToolbar.setVisible(true); + EditorGui.bringToFront(ShapeEdPreviewGui); + + ToolsPaletteArray->WorldEditorMove.performClick(); + %this.map.push(); + + // Switch to the ShapeEditor UndoManager + %this.oldUndoMgr = Editor.getUndoManager(); + Editor.setUndoManager( ShapeEdUndoManager ); + + ShapeEdShapeView.setDisplayType( EditorGui.currentDisplayType ); + %this.initStatusBar(); + + // Customise menu bar + %this.oldCamFitCmd = %this.replaceMenuCmd( "Camera", 8, "ShapeEdShapeView.fitToShape();" ); + %this.oldCamFitOrbitCmd = %this.replaceMenuCmd( "Camera", 9, "ShapeEdShapeView.fitToShape();" ); + + Parent::onActivated(%this); + } + + // Select the new shape + if (isObject(ShapeEditor.shape) && (ShapeEditor.shape.baseShape $= %filename)) + { + // Shape is already selected => re-highlight the selected material if necessary + ShapeEdMaterials.updateSelectedMaterial(ShapeEdMaterials-->highlightMaterial.getValue()); + } + else if (%filename !$= "") + { + ShapeEditor.selectShape(%filename, ShapeEditor.isDirty()); + + // 'fitToShape' only works after the GUI has been rendered, so force a repaint first + Canvas.repaint(); + ShapeEdShapeView.fitToShape(); + } +} + +function ShapeEditorPlugin::onActivated(%this) +{ + %this.open(""); + + // Try to start with the shape selected in the world editor + %count = EWorldEditor.getSelectionSize(); + for (%i = 0; %i < %count; %i++) + { + %obj = EWorldEditor.getSelectedObject(%i); + %shapeFile = ShapeEditor.getObjectShapeFile(%obj); + if (%shapeFile !$= "") + { + if (!isObject(ShapeEditor.shape) || (ShapeEditor.shape.baseShape !$= %shapeFile)) + { + // Call the 'onSelect' method directly if the object is not in the + // MissionGroup tree (such as a Player or Projectile object). + ShapeEdShapeTreeView.clearSelection(); + if (!ShapeEdShapeTreeView.selectItem(%obj)) + ShapeEdShapeTreeView.onSelect(%obj); + + // 'fitToShape' only works after the GUI has been rendered, so force a repaint first + Canvas.repaint(); + ShapeEdShapeView.fitToShape(); + } + break; + } + } +} + +function ShapeEditorPlugin::initStatusBar(%this) +{ + EditorGuiStatusBar.setInfo("Shape editor ( Shift Click ) to speed up camera."); + EditorGuiStatusBar.setSelection( ShapeEditor.shape.baseShape ); +} + +function ShapeEditorPlugin::onDeactivated(%this) +{ + %this.writeSettings(); + + // Notify game objects if shape has been modified + if ( ShapeEditor.isDirty() ) + ShapeEditor.shape.notifyShapeChanged(); + + $gfx::wireframe = $wasInWireFrameMode; + + ShapeEdMaterials.updateSelectedMaterial(false); + ShapeEditorToolbar.setVisible(false); + + ShapeEdPreviewGui.setVisible(false); + ShapeEdSelectWindow.setVisible(false); + ShapeEdPropWindow.setVisible(false); + ShapeEdAnimWindow.setVisible(false); + ShapeEdAdvancedWindow.setVisible(false); + + if( EditorGui-->MatEdPropertiesWindow.visible ) + { + ShapeEdMaterials.editSelectedMaterialEnd( true ); + } + + %this.map.pop(); + + // Restore the original undo manager + Editor.setUndoManager( %this.oldUndoMgr ); + + // Restore menu bar + %this.replaceMenuCmd( "Camera", 8, %this.oldCamFitCmd ); + %this.replaceMenuCmd( "Camera", 9, %this.oldCamFitOrbitCmd ); + + Parent::onDeactivated(%this); +} + +function ShapeEditorPlugin::onExitMission( %this ) +{ + // unselect the current shape + ShapeEdShapeView.setModel( "" ); + if (ShapeEditor.shape != -1) + ShapeEditor.shape.delete(); + ShapeEditor.shape = 0; + ShapeEdUndoManager.clearAll(); + ShapeEditor.setDirty( false ); + + ShapeEdSequenceList.clear(); + ShapeEdNodeTreeView.removeItem( 0 ); + ShapeEdPropWindow.update_onNodeSelectionChanged( -1 ); + ShapeEdDetailTree.removeItem( 0 ); + ShapeEdMaterialList.clear(); + + ShapeEdMountWindow-->mountList.clear(); + ShapeEdThreadWindow-->seqList.clear(); + ShapeEdThreadList.clear(); +} + +function ShapeEditorPlugin::openShape( %this, %path, %discardChangesToCurrent ) +{ + EditorGui.setEditor( ShapeEditorPlugin ); + + if( ShapeEditor.isDirty() && !%discardChangesToCurrent ) + { + MessageBoxYesNo( "Save Changes?", + "Save changes to current shape?", + "ShapeEditor.saveChanges(); ShapeEditorPlugin.openShape(\"" @ %path @ "\");", + "ShapeEditorPlugin.openShape(\"" @ %path @ "\");" ); + return; + } + + ShapeEditor.selectShape( %path ); + ShapeEdShapeView.fitToShape(); +} + +function shapeEditorWireframeMode() +{ + $gfx::wireframe = !$gfx::wireframe; + ShapeEditorToolbar-->wireframeMode.setStateOn($gfx::wireframe); +} + +//----------------------------------------------------------------------------- +// Settings +//----------------------------------------------------------------------------- + +function ShapeEditorPlugin::initSettings( %this ) +{ + EditorSettings.beginGroup( "ShapeEditor", true ); + + // Display options + EditorSettings.setDefaultValue( "BackgroundColor", "0 0 0 100" ); + EditorSettings.setDefaultValue( "HighlightMaterial", 1 ); + EditorSettings.setDefaultValue( "ShowNodes", 1 ); + EditorSettings.setDefaultValue( "ShowBounds", 0 ); + EditorSettings.setDefaultValue( "ShowObjBox", 1 ); + EditorSettings.setDefaultValue( "RenderMounts", 1 ); + EditorSettings.setDefaultValue( "RenderCollision", 0 ); + + // Grid + EditorSettings.setDefaultValue( "ShowGrid", 1 ); + EditorSettings.setDefaultValue( "GridSize", 0.1 ); + EditorSettings.setDefaultValue( "GridDimension", "40 40" ); + + // Sun + EditorSettings.setDefaultValue( "SunDiffuseColor", "255 255 255 255" ); + EditorSettings.setDefaultValue( "SunAmbientColor", "180 180 180 255" ); + EditorSettings.setDefaultValue( "SunAngleX", "45" ); + EditorSettings.setDefaultValue( "SunAngleZ", "135" ); + + // Sub-windows + EditorSettings.setDefaultValue( "AdvancedWndVisible", "1" ); + + EditorSettings.endGroup(); +} + +function ShapeEditorPlugin::readSettings( %this ) +{ + EditorSettings.beginGroup( "ShapeEditor", true ); + + // Display options + ShapeEdPreviewGui-->previewBackground.color = ColorIntToFloat( EditorSettings.value("BackgroundColor") ); + SetToggleButtonValue( ShapeEdMaterials-->highlightMaterial, EditorSettings.value( "HighlightMaterial" ) ); + SetToggleButtonValue( ShapeEditorToolbar-->showNodes, EditorSettings.value( "ShowNodes" ) ); + SetToggleButtonValue( ShapeEditorToolbar-->showBounds, EditorSettings.value( "ShowBounds" ) ); + SetToggleButtonValue( ShapeEditorToolbar-->showObjBox, EditorSettings.value( "ShowObjBox" ) ); + SetToggleButtonValue( ShapeEditorToolbar-->renderColMeshes, EditorSettings.value( "RenderCollision" ) ); + SetToggleButtonValue( ShapeEdMountWindow-->renderMounts, EditorSettings.value( "RenderMounts" ) ); + + // Grid + SetToggleButtonValue( ShapeEditorToolbar-->showGridBtn, EditorSettings.value( "ShowGrid" ) ); + ShapeEdShapeView.gridSize = EditorSettings.value( "GridSize" ); + ShapeEdShapeView.gridDimension = EditorSettings.value( "GridDimension" ); + + // Sun + ShapeEdShapeView.sunDiffuse = EditorSettings.value("SunDiffuseColor"); + ShapeEdShapeView.sunAmbient = EditorSettings.value("SunAmbientColor"); + + // Sub-windows + SetToggleButtonValue( ShapeEditorToolbar-->showAdvanced, EditorSettings.value( "AdvancedWndVisible" ) ); + + EditorSettings.endGroup(); +} + +function ShapeEditorPlugin::writeSettings( %this ) +{ + EditorSettings.beginGroup( "ShapeEditor", true ); + + // Display options + EditorSettings.setValue( "BackgroundColor", ColorFloatToInt( ShapeEdPreviewGui-->previewBackground.color ) ); + EditorSettings.setValue( "HighlightMaterial", ShapeEdMaterials-->highlightMaterial.getValue() ); + EditorSettings.setValue( "ShowNodes", ShapeEditorToolbar-->showNodes.getValue() ); + EditorSettings.setValue( "ShowBounds", ShapeEditorToolbar-->showBounds.getValue() ); + EditorSettings.setValue( "ShowObjBox", ShapeEditorToolbar-->showObjBox.getValue() ); + EditorSettings.setValue( "RenderCollision", ShapeEditorToolbar-->renderColMeshes.getValue() ); + EditorSettings.setValue( "RenderMounts", ShapeEdMountWindow-->renderMounts.getValue() ); + + // Grid + EditorSettings.setValue( "ShowGrid", ShapeEditorToolbar-->showGridBtn.getValue() ); + EditorSettings.setValue( "GridSize", ShapeEdShapeView.gridSize ); + EditorSettings.setValue( "GridDimension", ShapeEdShapeView.gridDimension ); + + // Sun + EditorSettings.setValue( "SunDiffuseColor", ShapeEdShapeView.sunDiffuse ); + EditorSettings.setValue( "SunAmbientColor", ShapeEdShapeView.sunAmbient ); + EditorSettings.setValue( "SunAngleX", ShapeEdShapeView.sunAngleX ); + EditorSettings.setValue( "SunAngleZ", ShapeEdShapeView.sunAngleZ ); + + // Sub-windows + EditorSettings.setValue( "AdvancedWndVisible", ShapeEditorToolbar-->showAdvanced.getValue() ); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditor.ed.cs b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditor.ed.cs new file mode 100644 index 000000000..d79923776 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditor.ed.cs @@ -0,0 +1,3395 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// @todo: +// +// - split node transform editboxes into X Y Z and rot X Y Z with spin controls +// to allow easier manual editing +// - add groundspeed editing ( use same format as node transform editing ) +// +// Known bugs/limitations: +// +// - resizing the GuiTextListCtrl should resize the columns as well +// - modifying the from/in/out properties of a sequence will change the sequence +// order in the shape ( since it results in remove/add sequence commands ) +// - deleting a node should not delete its children as well? +// + +//------------------------------------------------------------------------------ +// Utility Methods +//------------------------------------------------------------------------------ + +if ( !isObject( ShapeEditor ) ) new ScriptObject( ShapeEditor ) +{ + shape = -1; + deletedCount = 0; +}; + + +// Capitalise the first letter of the input string +function strcapitalise( %str ) +{ + %len = strlen( %str ); + return strupr( getSubStr( %str,0,1 ) ) @ getSubStr( %str,1,%len-1 ); +} + +function ShapeEditor::getObjectShapeFile( %this, %obj ) +{ + // Get the path to the shape file used by the given object (not perfect, but + // works for the vast majority of object types) + %path = ""; + if ( %obj.isMemberOfClass( "TSStatic" ) ) + %path = %obj.shapeName; + else if ( %obj.isMemberOfClass( "PhysicsShape" ) ) + %path = %obj.getDataBlock().shapeName; + else if ( %obj.isMemberOfClass( "GameBase" ) ) + %path = %obj.getDataBlock().shapeFile; + + return %path; +} + +// Check if the given name already exists +function ShapeEditor::nameExists( %this, %type, %name ) +{ + if ( ShapeEditor.shape == -1 ) + return false; + + if ( %type $= "node" ) + return ( ShapeEditor.shape.getNodeIndex( %name ) >= 0 ); + else if ( %type $= "sequence" ) + return ( ShapeEditor.shape.getSequenceIndex( %name ) >= 0 ); + else if ( %type $= "object" ) + return ( ShapeEditor.shape.getObjectIndex( %name ) >= 0 ); +} + +// Check if the given 'hint' name exists (spaces could also be underscores) +function ShapeEditor::hintNameExists( %this, %type, %name ) +{ + if ( ShapeEditor.nameExists( %type, %name ) ) + return true; + + // If the name contains spaces, try replacing with underscores + %name = strreplace( %name, " ", "_" ); + if ( ShapeEditor.nameExists( %type, %name ) ) + return true; + + return false; +} + +// Generate a unique name from a given base by appending an integer +function ShapeEditor::getUniqueName( %this, %type, %name ) +{ + for ( %idx = 1; %idx < 100; %idx++ ) + { + %uniqueName = %name @ %idx; + if ( !%this.nameExists( %type, %uniqueName ) ) + break; + } + + return %uniqueName; +} + +function ShapeEditor::getProxyName( %this, %seqName ) +{ + return "__proxy__" @ %seqName; +} + +function ShapeEditor::getUnproxyName( %this, %proxyName ) +{ + return strreplace( %proxyName, "__proxy__", "" ); +} + +function ShapeEditor::getBackupName( %this, %seqName ) +{ + return "__backup__" @ %seqName; +} + +// Check if this mesh name is a collision hint +function ShapeEditor::isCollisionMesh( %this, %name ) +{ + return ( startswith( %name, "ColBox" ) || + startswith( %name, "ColSphere" ) || + startswith( %name, "ColCapsule" ) || + startswith( %name, "ColConvex" ) ); +} + +// +function ShapeEditor::getSequenceSource( %this, %seqName ) +{ + %source = %this.shape.getSequenceSource( %seqName ); + + // Use the sequence name as the source for DTS built-in sequences + %src0 = getField( %source, 0 ); + %src1 = getField( %source, 1 ); + if ( %src0 $= %src1 ) + %source = setField( %source, 1, "" ); + if ( %src0 $= "" ) + %source = setField( %source, 0, %seqName ); + + return %source; +} + +// Recursively get names for a node and its children +function ShapeEditor::getNodeNames( %this, %nodeName, %names, %exclude ) +{ + if ( %nodeName $= %exclude ) + return %names; + + %count = %this.shape.getNodeChildCount( %nodeName ); + for ( %i = 0; %i < %count; %i++ ) + { + %childName = %this.shape.getNodeChildName( %nodeName, %i ); + %names = %this.getNodeNames( %childName, %names, %exclude ); + } + + %names = %names TAB %nodeName; + + return trim( %names ); +} + +// Get the list of meshes for a particular object +function ShapeEditor::getObjectMeshList( %this, %name ) +{ + %list = ""; + %count = %this.shape.getMeshCount( %name ); + for ( %i = 0; %i < %count; %i++ ) + %list = %list TAB %this.shape.getMeshName( %name, %i ); + return trim( %list ); +} + +// Get the list of meshes for a particular detail level +function ShapeEditor::getDetailMeshList( %this, %detSize ) +{ + %list = ""; + %objCount = ShapeEditor.shape.getObjectCount(); + for ( %i = 0; %i < %objCount; %i++ ) + { + %objName = ShapeEditor.shape.getObjectName( %i ); + %meshCount = ShapeEditor.shape.getMeshCount( %objName ); + for ( %j = 0; %j < %meshCount; %j++ ) + { + %size = ShapeEditor.shape.getMeshSize( %objName, %j ); + if ( %size == %detSize ) + %list = %list TAB %this.shape.getMeshName( %objName, %j ); + } + } + return trim( %list ); +} + +function ShapeEditor::isDirty( %this ) +{ + return ( isObject( %this.shape ) && ShapeEdPropWindow-->saveBtn.isActive() ); +} + +function ShapeEditor::setDirty( %this, %dirty ) +{ + if ( %dirty ) + ShapeEdSelectWindow.text = "Shapes *"; + else + ShapeEdSelectWindow.text = "Shapes"; + + ShapeEdPropWindow-->saveBtn.setActive( %dirty ); +} + +function ShapeEditor::saveChanges( %this ) +{ + if ( isObject( ShapeEditor.shape ) ) + { + ShapeEditor.saveConstructor( ShapeEditor.shape ); + ShapeEditor.shape.writeChangeSet(); + ShapeEditor.shape.notifyShapeChanged(); // Force game objects to reload shape + ShapeEditor.setDirty( false ); + } +} + +//------------------------------------------------------------------------------ +// Shape Selection +//------------------------------------------------------------------------------ + +function ShapeEditor::findConstructor( %this, %path ) +{ + %count = TSShapeConstructorGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = TSShapeConstructorGroup.getObject( %i ); + if ( %obj.baseShape $= %path ) + return %obj; + } + return -1; +} + +function ShapeEditor::createConstructor( %this, %path ) +{ + %name = strcapitalise( fileBase( %path ) ) @ strcapitalise( getSubStr( fileExt( %path ), 1, 3 ) ); + %name = strreplace( %name, "-", "_" ); + %name = strreplace( %name, ".", "_" ); + %name = getUniqueName( %name ); + return new TSShapeConstructor( %name ) { baseShape = %path; }; +} + +function ShapeEditor::saveConstructor( %this, %constructor ) +{ + %savepath = filePath( %constructor.baseShape ) @ "/" @ fileBase( %constructor.baseShape ) @ ".cs"; + new PersistenceManager( shapeEd_perMan ); + shapeEd_perMan.setDirty( %constructor, %savepath ); + shapeEd_perMan.saveDirtyObject( %constructor ); + shapeEd_perMan.delete(); +} + +// Handle a selection in the shape selector list +function ShapeEdSelectWindow::onSelect( %this, %path ) +{ + // Prompt user to save the old shape if it is dirty + if ( ShapeEditor.isDirty() ) + { + %cmd = "ColladaImportDlg.showDialog( \"" @ %path @ "\", \"ShapeEditor.selectShape( \\\"" @ %path @ "\\\", "; + MessageBoxYesNoCancel( "Shape Modified", "Would you like to save your changes?", %cmd @ "true );\" );", %cmd @ "false );\" );" ); + } + else + { + %cmd = "ShapeEditor.selectShape( \"" @ %path @ "\", false );"; + ColladaImportDlg.showDialog( %path, %cmd ); + } +} + +function ShapeEditor::selectShape( %this, %path, %saveOld ) +{ + ShapeEdShapeView.setModel( "" ); + + if ( %saveOld ) + { + // Save changes to a TSShapeConstructor script + %this.saveChanges(); + } + else if ( ShapeEditor.isDirty() ) + { + // Purge all unsaved changes + %oldPath = ShapeEditor.shape.baseShape; + ShapeEditor.shape.delete(); + ShapeEditor.shape = 0; + + reloadResource( %oldPath ); // Force game objects to reload shape + } + + // Initialise the shape preview window + if ( !ShapeEdShapeView.setModel( %path ) ) + { + MessageBoxOK( "Error", "Failed to load '" @ %path @ "'. Check the console for error messages." ); + return; + } + ShapeEdShapeView.fitToShape(); + + ShapeEdUndoManager.clearAll(); + ShapeEditor.setDirty( false ); + + // Get ( or create ) the TSShapeConstructor object for this shape + ShapeEditor.shape = ShapeEditor.findConstructor( %path ); + if ( ShapeEditor.shape <= 0 ) + { + ShapeEditor.shape = %this.createConstructor( %path ); + if ( ShapeEditor.shape <= 0 ) + { + error( "ShapeEditor: Error - could not select " @ %path ); + return; + } + } + + // Initialise the editor windows + ShapeEdAdvancedWindow.update_onShapeSelectionChanged(); + ShapeEdMountWindow.update_onShapeSelectionChanged(); + ShapeEdThreadWindow.update_onShapeSelectionChanged(); + ShapeEdColWindow.update_onShapeSelectionChanged(); + ShapeEdPropWindow.update_onShapeSelectionChanged(); + ShapeEdShapeView.refreshShape(); + + // Update object type hints + ShapeEdSelectWindow.updateHints(); + + // Update editor status bar + EditorGuiStatusBar.setSelection( %path ); +} + +// Handle a selection in the MissionGroup shape selector +function ShapeEdShapeTreeView::onSelect( %this, %obj ) +{ + %path = ShapeEditor.getObjectShapeFile( %obj ); + if ( %path !$= "" ) + ShapeEdSelectWindow.onSelect( %path ); + + // Set the object type (for required nodes and sequences display) + %objClass = %obj.getClassName(); + %hintId = -1; + + %count = ShapeHintGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %hint = ShapeHintGroup.getObject( %i ); + if ( %objClass $= %hint.objectType ) + { + %hintId = %hint; + break; + } + else if ( isMemberOfClass( %objClass, %hint.objectType ) ) + { + %hintId = %hint; + } + } + ShapeEdHintMenu.setSelected( %hintId ); +} + +// Find all DTS or COLLADA models. Note: most of this section was shamelessly +// stolen from creater.ed.cs => great work whoever did the original! +function ShapeEdSelectWindow::navigate( %this, %address ) +{ + // Freeze the icon array so it doesn't update until we've added all of the + // icons + %this-->shapeLibrary.frozen = true; + %this-->shapeLibrary.clear(); + ShapeEdSelectMenu.clear(); + + %filePatterns = getFormatExtensions(); + %fullPath = findFirstFileMultiExpr( %filePatterns ); + + while ( %fullPath !$= "" ) + { + // Ignore cached DTS files + if ( endswith( %fullPath, "cached.dts" ) ) + { + %fullPath = findNextFileMultiExpr( %filePatterns ); + continue; + } + + // Ignore assets in the tools folder + %fullPath = makeRelativePath( %fullPath, getMainDotCSDir() ); + %splitPath = strreplace( %fullPath, " ", "_" ); + %splitPath = strreplace( %splitPath, "/", " " ); + if ( getWord( %splitPath, 0 ) $= "tools" ) + { + %fullPath = findNextFileMultiExpr( %filePatterns ); + continue; + } + + %dirCount = getWordCount( %splitPath ) - 1; + %pathFolders = getWords( %splitPath, 0, %dirCount - 1 ); + + // Add this file's path ( parent folders ) to the + // popup menu if it isn't there yet. + %temp = strreplace( %pathFolders, " ", "/" ); + %temp = strreplace( %temp, "_", " " ); + %r = ShapeEdSelectMenu.findText( %temp ); + if ( %r == -1 ) + ShapeEdSelectMenu.add( %temp ); + + // Is this file in the current folder? + if ( stricmp( %pathFolders, %address ) == 0 ) + { + %this.addShapeIcon( %fullPath ); + } + // Then is this file in a subfolder we need to add + // a folder icon for? + else + { + %wordIdx = 0; + %add = false; + + if ( %address $= "" ) + { + %add = true; + %wordIdx = 0; + } + else + { + for ( ; %wordIdx < %dirCount; %wordIdx++ ) + { + %temp = getWords( %splitPath, 0, %wordIdx ); + if ( stricmp( %temp, %address ) == 0 ) + { + %add = true; + %wordIdx++; + break; + } + } + } + + if ( %add == true ) + { + %folder = getWord( %splitPath, %wordIdx ); + + // Add folder icon if not already present + %ctrl = %this.findIconCtrl( %folder ); + if ( %ctrl == -1 ) + %this.addFolderIcon( %folder ); + } + } + + %fullPath = findNextFileMultiExpr( %filePatterns ); + } + + %this-->shapeLibrary.sort( "alphaIconCompare" ); + for ( %i = 0; %i < %this-->shapeLibrary.getCount(); %i++ ) + %this-->shapeLibrary.getObject( %i ).autoSize = false; + + %this-->shapeLibrary.frozen = false; + %this-->shapeLibrary.refresh(); + %this.address = %address; + + ShapeEdSelectMenu.sort(); + + %str = strreplace( %address, " ", "/" ); + %r = ShapeEdSelectMenu.findText( %str ); + if ( %r != -1 ) + ShapeEdSelectMenu.setSelected( %r, false ); + else + ShapeEdSelectMenu.setText( %str ); +} + +function ShapeEdSelectWindow::navigateDown( %this, %folder ) +{ + if ( %this.address $= "" ) + %address = %folder; + else + %address = %this.address SPC %folder; + + // Because this is called from an IconButton::onClick command + // we have to wait a tick before actually calling navigate, else + // we would delete the button out from under itself. + %this.schedule( 1, "navigate", %address ); +} + +function ShapeEdSelectWindow::navigateUp( %this ) +{ + %count = getWordCount( %this.address ); + + if ( %count == 0 ) + return; + + if ( %count == 1 ) + %address = ""; + else + %address = getWords( %this.address, 0, %count - 2 ); + + %this.navigate( %address ); +} + +function ShapeEdSelectWindow::findIconCtrl( %this, %name ) +{ + for ( %i = 0; %i < %this-->shapeLibrary.getCount(); %i++ ) + { + %ctrl = %this-->shapeLibrary.getObject( %i ); + if ( %ctrl.text $= %name ) + return %ctrl; + } + return -1; +} + +function ShapeEdSelectWindow::createIcon( %this ) +{ + %ctrl = new GuiIconButtonCtrl() + { + profile = "GuiCreatorIconButtonProfile"; + iconLocation = "Left"; + textLocation = "Right"; + extent = "348 19"; + textMargin = 8; + buttonMargin = "2 2"; + autoSize = false; + sizeIconToButton = true; + makeIconSquare = true; + buttonType = "radioButton"; + groupNum = "-1"; + }; + + return %ctrl; +} + +function ShapeEdSelectWindow::addFolderIcon( %this, %text ) +{ + %ctrl = %this.createIcon(); + + %ctrl.altCommand = "ShapeEdSelectWindow.navigateDown( \"" @ %text @ "\" );"; + %ctrl.iconBitmap = "tools/gui/images/folder.png"; + %ctrl.text = %text; + %ctrl.tooltip = %text; + %ctrl.class = "CreatorFolderIconBtn"; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this-->shapeLibrary.addGuiControl( %ctrl ); +} + +function ShapeEdSelectWindow::addShapeIcon( %this, %fullPath ) +{ + %ctrl = %this.createIcon(); + + %ext = fileExt( %fullPath ); + %file = fileBase( %fullPath ); + %fileLong = %file @ %ext; + %tip = %fileLong NL + "Size: " @ fileSize( %fullPath ) / 1000.0 SPC "KB" NL + "Date Created: " @ fileCreatedTime( %fullPath ) NL + "Last Modified: " @ fileModifiedTime( %fullPath ); + + %ctrl.altCommand = "ShapeEdSelectWindow.onSelect( \"" @ %fullPath @ "\" );"; + %ctrl.iconBitmap = ( ( %ext $= ".dts" ) ? EditorIconRegistry::findIconByClassName( "TSStatic" ) : "tools/gui/images/iconCollada" ); + %ctrl.text = %file; + %ctrl.class = "CreatorStaticIconBtn"; + %ctrl.tooltip = %tip; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + // Check if a shape specific icon is available + %formats = ".png .jpg .dds .bmp .gif .jng .tga"; + %count = getWordCount( %formats ); + for ( %i = 0; %i < %count; %i++ ) + { + %ext = getWord( %formats, %i ); + if ( isFile( %fullPath @ %ext ) ) + { + %ctrl.iconBitmap = %fullPath @ %ext; + break; + } + } + + %this-->shapeLibrary.addGuiControl( %ctrl ); +} + +function ShapeEdSelectMenu::onSelect( %this, %id, %text ) +{ + %split = strreplace( %text, "/", " " ); + ShapeEdSelectWindow.navigate( %split ); +} + +// Update the GUI in response to the shape selection changing +function ShapeEdPropWindow::update_onShapeSelectionChanged( %this ) +{ + // --- NODES TAB --- + ShapeEdNodeTreeView.removeItem( 0 ); + %rootId = ShapeEdNodeTreeView.insertItem( 0, "<root>", 0, "" ); + %count = ShapeEditor.shape.getNodeCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %name = ShapeEditor.shape.getNodeName( %i ); + if ( ShapeEditor.shape.getNodeParentName( %name ) $= "" ) + ShapeEdNodeTreeView.addNodeTree( %name ); + } + %this.update_onNodeSelectionChanged( -1 ); // no node selected + + // --- SEQUENCES TAB --- + ShapeEdSequenceList.clear(); + ShapeEdSequenceList.addRow( -1, "Name" TAB "Cyclic" TAB "Blend" TAB "Frames" TAB "Priority" ); + ShapeEdSequenceList.setRowActive( -1, false ); + ShapeEdSequenceList.addRow( 0, "<rootpose>" TAB "" TAB "" TAB "" TAB "" ); + + %count = ShapeEditor.shape.getSequenceCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %name = ShapeEditor.shape.getSequenceName( %i ); + + // Ignore __backup__ sequences (only used by editor) + if ( !startswith( %name, "__backup__" ) ) + ShapeEdSequenceList.addItem( %name ); + } + ShapeEdThreadWindow.onAddThread(); // add thread 0 + + // --- DETAILS TAB --- + // Add detail levels and meshes to tree + ShapeEdDetailTree.clearSelection(); + ShapeEdDetailTree.removeItem( 0 ); + %root = ShapeEdDetailTree.insertItem( 0, "<root>", "", "" ); + %objCount = ShapeEditor.shape.getObjectCount(); + for ( %i = 0; %i < %objCount; %i++ ) + { + %objName = ShapeEditor.shape.getObjectName( %i ); + %meshCount = ShapeEditor.shape.getMeshCount( %objName ); + for ( %j = 0; %j < %meshCount; %j++ ) + { + %meshName = ShapeEditor.shape.getMeshName( %objName, %j ); + ShapeEdDetailTree.addMeshEntry( %meshName, 1 ); + } + } + + // Initialise object node list + ShapeEdDetails-->objectNode.clear(); + ShapeEdDetails-->objectNode.add( "<root>" ); + %nodeCount = ShapeEditor.shape.getNodeCount(); + for ( %i = 0; %i < %nodeCount; %i++ ) + ShapeEdDetails-->objectNode.add( ShapeEditor.shape.getNodeName( %i ) ); + + // --- MATERIALS TAB --- + ShapeEdMaterials.updateMaterialList(); +} + +//------------------------------------------------------------------------------ +// Shape Hints +//------------------------------------------------------------------------------ + +function ShapeEdHintMenu::onSelect( %this, %id, %text ) +{ + ShapeEdSelectWindow.updateHints(); +} + +function ShapeEdSelectWindow::updateHints( %this ) +{ + %objectType = ShapeEdHintMenu.getText(); + + ShapeEdSelectWindow-->nodeHints.freeze( true ); + ShapeEdSelectWindow-->sequenceHints.freeze( true ); + + // Move all current hint controls to a holder SimGroup + for ( %i = ShapeEdSelectWindow-->nodeHints.getCount()-1; %i >= 0; %i-- ) + ShapeHintControls.add( ShapeEdSelectWindow-->nodeHints.getObject( %i ) ); + for ( %i = ShapeEdSelectWindow-->sequenceHints.getCount()-1; %i >= 0; %i-- ) + ShapeHintControls.add( ShapeEdSelectWindow-->sequenceHints.getObject( %i ) ); + + // Update node and sequence hints, modifying and/or creating gui controls as needed + for ( %i = 0; %i < ShapeHintGroup.getCount(); %i++ ) + { + %hint = ShapeHintGroup.getObject( %i ); + if ( ( %objectType $= %hint.objectType ) || isMemberOfClass( %objectType, %hint.objectType ) ) + { + for ( %idx = 0; %hint.node[%idx] !$= ""; %idx++ ) + ShapeEdHintMenu.processHint( "node", %hint.node[%idx] ); + + for ( %idx = 0; %hint.sequence[%idx] !$= ""; %idx++ ) + ShapeEdHintMenu.processHint( "sequence", %hint.sequence[%idx] ); + } + } + + ShapeEdSelectWindow-->nodeHints.freeze( false ); + ShapeEdSelectWindow-->nodeHints.updateStack(); + ShapeEdSelectWindow-->sequenceHints.freeze( false ); + ShapeEdSelectWindow-->sequenceHints.updateStack(); + +} + +function ShapeEdHintMenu::processHint( %this, %type, %hint ) +{ + %name = getField( %hint, 0 ); + %desc = getField( %hint, 1 ); + + // check for arrayed names (ending in 0-N or 1-N) + %pos = strstr( %name, "0-" ); + if ( %pos == -1 ) + %pos = strstr( %name, "1-" ); + + if ( %pos > 0 ) + { + // arrayed name => add controls for each name in the array, but collapse + // consecutive indices where possible. eg. if the model only has nodes + // mount1-3, we should create: mount0 (red), mount1-3 (green), mount4-31 (red) + %base = getSubStr( %name, 0, %pos ); // array name + %first = getSubStr( %name, %pos, 1 ); // first index + %last = getSubStr( %name, %pos+2, 3 ); // last index + + // get the state of the first element + %arrayStart = %first; + %prevPresent = ShapeEditor.hintNameExists( %type, %base @ %first ); + + for ( %j = %first + 1; %j <= %last; %j++ ) + { + // if the state of this element is different to the previous one, we + // need to add a hint + %present = ShapeEditor.hintNameExists( %type, %base @ %j ); + if ( %present != %prevPresent ) + { + ShapeEdSelectWindow.addObjectHint( %type, %base, %desc, %prevPresent, %arrayStart, %j-1 ); + %arrayStart = %j; + %prevPresent = %present; + } + } + + // add hint for the last group + ShapeEdSelectWindow.addObjectHint( %type, %base, %desc, %prevPresent, %arrayStart, %last ); + } + else + { + // non-arrayed name + %present = ShapeEditor.hintNameExists( %type, %name ); + ShapeEdSelectWindow.addObjectHint( %type, %name, %desc, %present ); + } +} + +function ShapeEdSelectWindow::addObjectHint( %this, %type, %name, %desc, %present, %start, %end ) +{ + // Get a hint gui control (create one if needed) + if ( ShapeHintControls.getCount() == 0 ) + { + // Create a new hint gui control + %ctrl = new GuiIconButtonCtrl() + { + profile = "GuiCreatorIconButtonProfile"; + iconLocation = "Left"; + textLocation = "Right"; + extent = "348 19"; + textMargin = 8; + buttonMargin = "2 2"; + autoSize = true; + buttonType = "radioButton"; + groupNum = "-1"; + iconBitmap = "tools/editorClasses/gui/images/iconCancel"; + text = "hint"; + tooltip = ""; + }; + + ShapeHintControls.add( %ctrl ); + } + %ctrl = ShapeHintControls.getObject( 0 ); + + // Initialise the control, then add it to the appropriate list + %name = %name @ %start; + if ( %end !$= %start ) + %ctrl.text = %name @ "-" @ %end; + else + %ctrl.text = %name; + + %ctrl.tooltip = %desc; + %ctrl.setBitmap( "tools/editorClasses/gui/images/" @ ( %present ? "iconAccept" : "iconCancel" ) ); + %ctrl.setStateOn( false ); + %ctrl.resetState(); + + switch$ ( %type ) + { + case "node": + %ctrl.altCommand = %present ? "" : "ShapeEdNodes.onAddNode( \"" @ %name @ "\" );"; + ShapeEdSelectWindow-->nodeHints.addGuiControl( %ctrl ); + case "sequence": + %ctrl.altCommand = %present ? "" : "ShapeEdSequences.onAddSequence( \"" @ %name @ "\" );"; + ShapeEdSelectWindow-->sequenceHints.addGuiControl( %ctrl ); + } +} + +//------------------------------------------------------------------------------ + +function ShapeEdSeqNodeTabBook::onTabSelected( %this, %name, %index ) +{ + %this.activePage = %name; + + switch$ ( %name ) + { + case "Seq": + ShapeEdPropWindow-->newBtn.ToolTip = "Add new sequence"; + ShapeEdPropWindow-->newBtn.Command = "ShapeEdSequences.onAddSequence();"; + ShapeEdPropWindow-->newBtn.setActive( true ); + ShapeEdPropWindow-->deleteBtn.ToolTip = "Delete selected sequence (cannot be undone)"; + ShapeEdPropWindow-->deleteBtn.Command = "ShapeEdSequences.onDeleteSequence();"; + ShapeEdPropWindow-->deleteBtn.setActive( true ); + + case "Node": + ShapeEdPropWindow-->newBtn.ToolTip = "Add new node"; + ShapeEdPropWindow-->newBtn.Command = "ShapeEdNodes.onAddNode();"; + ShapeEdPropWindow-->newBtn.setActive( true ); + ShapeEdPropWindow-->deleteBtn.ToolTip = "Delete selected node (cannot be undone)"; + ShapeEdPropWindow-->deleteBtn.Command = "ShapeEdNodes.onDeleteNode();"; + ShapeEdPropWindow-->deleteBtn.setActive( true ); + + case "Detail": + ShapeEdPropWindow-->newBtn.ToolTip = ""; + ShapeEdPropWindow-->newBtn.Command = ""; + ShapeEdPropWindow-->newBtn.setActive( false ); + ShapeEdPropWindow-->deleteBtn.ToolTip = "Delete the selected mesh or detail level (cannot be undone)"; + ShapeEdPropWindow-->deleteBtn.Command = "ShapeEdDetails.onDeleteMesh();"; + ShapeEdPropWindow-->deleteBtn.setActive( true ); + + case "Mat": + ShapeEdPropWindow-->newBtn.ToolTip = ""; + ShapeEdPropWindow-->newBtn.Command = ""; + ShapeEdPropWindow-->newBtn.setActive( false ); + ShapeEdPropWindow-->deleteBtn.ToolTip = ""; + ShapeEdPropWindow-->deleteBtn.Command = ""; + ShapeEdPropWindow-->deleteBtn.setActive( false ); + + // For some reason, the header is not resized correctly until the Materials tab has been + // displayed at least once, so resize it here too + ShapeEdMaterials-->materialListHeader.setExtent( getWord( ShapeEdMaterialList.extent, 0 ) SPC "19" ); + } +} + +//------------------------------------------------------------------------------ +// Node Editing +//------------------------------------------------------------------------------ + +// Update the GUI in response to the node selection changing +function ShapeEdPropWindow::update_onNodeSelectionChanged( %this, %id ) +{ + if ( %id > 0 ) + { + // Enable delete button and edit boxes + if ( ShapeEdSeqNodeTabBook.activePage $= "Node" ) + ShapeEdPropWindow-->deleteBtn.setActive( true ); + ShapeEdNodes-->nodeName.setActive( true ); + ShapeEdNodes-->nodePosition.setActive( true ); + ShapeEdNodes-->nodeRotation.setActive( true ); + + // Update the node inspection data + %name = ShapeEdNodeTreeView.getItemText( %id ); + + ShapeEdNodes-->nodeName.setText( %name ); + + // Node parent list => ancestor and sibling nodes only (can't re-parent to a descendent) + ShapeEdNodeParentMenu.clear(); + %parentNames = ShapeEditor.getNodeNames( "", "<root>", %name ); + %count = getWordCount( %parentNames ); + for ( %i = 0; %i < %count; %i++ ) + ShapeEdNodeParentMenu.add( getWord(%parentNames, %i), %i ); + + %pName = ShapeEditor.shape.getNodeParentName( %name ); + if ( %pName $= "" ) + %pName = "<root>"; + ShapeEdNodeParentMenu.setText( %pName ); + + if ( ShapeEdNodes-->worldTransform.getValue() ) + { + // Global transform + %txfm = ShapeEditor.shape.getNodeTransform( %name, 1 ); + ShapeEdNodes-->nodePosition.setText( getWords( %txfm, 0, 2 ) ); + ShapeEdNodes-->nodeRotation.setText( getWords( %txfm, 3, 6 ) ); + } + else + { + // Local transform (relative to parent) + %txfm = ShapeEditor.shape.getNodeTransform( %name, 0 ); + ShapeEdNodes-->nodePosition.setText( getWords( %txfm, 0, 2 ) ); + ShapeEdNodes-->nodeRotation.setText( getWords( %txfm, 3, 6 ) ); + } + + ShapeEdShapeView.selectedNode = ShapeEditor.shape.getNodeIndex( %name ); + } + else + { + // Disable delete button and edit boxes + if ( ShapeEdSeqNodeTabBook.activePage $= "Node" ) + ShapeEdPropWindow-->deleteBtn.setActive( false ); + ShapeEdNodes-->nodeName.setActive( false ); + ShapeEdNodes-->nodePosition.setActive( false ); + ShapeEdNodes-->nodeRotation.setActive( false ); + + ShapeEdNodes-->nodeName.setText( "" ); + ShapeEdNodes-->nodePosition.setText( "" ); + ShapeEdNodes-->nodeRotation.setText( "" ); + + ShapeEdShapeView.selectedNode = -1; + } +} + +// Update the GUI in response to a node being added +function ShapeEdPropWindow::update_onNodeAdded( %this, %nodeName, %oldTreeIndex ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.updateNodeTransforms(); + ShapeEdSelectWindow.updateHints(); + + // --- MOUNT WINDOW --- + if ( ShapeEdMountWindow.isMountableNode( %nodeName ) ) + { + ShapeEdMountWindow-->mountNode.add( %nodeName ); + ShapeEdMountWindow-->mountNode.sort(); + } + + // --- NODES TAB --- + %id = ShapeEdNodeTreeView.addNodeTree( %nodeName ); + if ( %oldTreeIndex <= 0 ) + { + // This is a new node => make it the current selection + if ( %id > 0 ) + { + ShapeEdNodeTreeView.clearSelection(); + ShapeEdNodeTreeView.selectItem( %id ); + } + } + else + { + // This node has been un-deleted. Inserting a new item puts it at the + // end of the siblings, but we want to restore the original order as + // if the item was never deleted, so move it up as required. + %childIndex = ShapeEdNodeTreeView.getChildIndexByName( %nodeName ); + while ( %childIndex > %oldTreeIndex ) + { + ShapeEdNodeTreeView.moveItemUp( %id ); + %childIndex--; + } + } + + // --- DETAILS TAB --- + ShapeEdDetails-->objectNode.add( %nodeName ); +} + +// Update the GUI in response to a node(s) being removed +function ShapeEdPropWindow::update_onNodeRemoved( %this, %nameList, %nameCount ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.updateNodeTransforms(); + ShapeEdSelectWindow.updateHints(); + + // Remove nodes from the mountable list, and any shapes mounted to the node + for ( %i = 0; %i < %nameCount; %i++ ) + { + %nodeName = getField( %nameList, %i ); + ShapeEdMountWindow-->mountNode.clearEntry( ShapeEdMountWindow-->mountNode.findText( %nodeName ) ); + + for ( %j = ShapeEdMountWindow-->mountList.rowCount()-1; %j >= 1; %j-- ) + { + %text = ShapeEdMountWindow-->mountList.getRowText( %j ); + if ( getField( %text, 1 ) $= %nodeName ) + { + ShapeEdShapeView.unmountShape( %j-1 ); + ShapeEdMountWindow-->mountList.removeRow( %j ); + } + } + } + + // --- NODES TAB --- + %lastName = getField( %nameList, %nameCount-1 ); + %id = ShapeEdNodeTreeView.findItemByName( %lastName ); // only need to remove the parent item + if ( %id > 0 ) + { + ShapeEdNodeTreeView.removeItem( %id ); + if ( ShapeEdNodeTreeView.getSelectedItem() <= 0 ) + ShapeEdPropWindow.update_onNodeSelectionChanged( -1 ); + } + + // --- DETAILS TAB --- + for ( %i = 0; %i < %nameCount; %i++ ) + { + %nodeName = getField( %nameList, %i ); + ShapeEdDetails-->objectNode.clearEntry( ShapeEdDetails-->objectNode.findText( %nodeName ) ); + } +} + +// Update the GUI in response to a node being renamed +function ShapeEdPropWindow::update_onNodeRenamed( %this, %oldName, %newName ) +{ + // --- MISC --- + ShapeEdSelectWindow.updateHints(); + + // --- MOUNT WINDOW --- + // Update entries for any shapes mounted to this node + %rowCount = ShapeEdMountWindow-->mountList.rowCount(); + for ( %i = 1; %i < %rowCount; %i++ ) + { + %text = ShapeEdMountWindow-->mountList.getRowText( %i ); + if ( getField( %text, 1 ) $= %oldName ) + { + %text = setField( %text, 1, %newName ); + ShapeEdMountWindow-->mountList.setRowById( ShapeEdMountWindow-->mountList.getRowId( %i ), %text ); + } + } + + // Update list of mountable nodes + ShapeEdMountWindow-->mountNode.clearEntry( ShapeEdMountWindow-->mountNode.findText( %oldName ) ); + if ( ShapeEdMountWindow.isMountableNode( %newName ) ) + { + ShapeEdMountWindow-->mountNode.add( %newName ); + ShapeEdMountWindow-->mountNode.sort(); + } + + // --- NODES TAB --- + %id = ShapeEdNodeTreeView.findItemByName( %oldName ); + ShapeEdNodeTreeView.editItem( %id, %newName, 0 ); + if ( ShapeEdNodeTreeView.getSelectedItem() == %id ) + ShapeEdNodes-->nodeName.setText( %newName ); + + // --- DETAILS TAB --- + %id = ShapeEdDetails-->objectNode.findText( %oldName ); + if ( %id != -1 ) + { + ShapeEdDetails-->objectNode.clearEntry( %id ); + ShapeEdDetails-->objectNode.add( %newName, %id ); + ShapeEdDetails-->objectNode.sortID(); + if ( ShapeEdDetails-->objectNode.getText() $= %oldName ) + ShapeEdDetails-->objectNode.setText( %newName ); + } +} + +// Update the GUI in response to a node's parent being changed +function ShapeEdPropWindow::update_onNodeParentChanged( %this, %nodeName ) +{ + // --- MISC --- + ShapeEdShapeView.updateNodeTransforms(); + + // --- NODES TAB --- + %isSelected = 0; + %id = ShapeEdNodeTreeView.findItemByName( %nodeName ); + if ( %id > 0 ) + { + %isSelected = ( ShapeEdNodeTreeView.getSelectedItem() == %id ); + ShapeEdNodeTreeView.removeItem( %id ); + } + ShapeEdNodeTreeView.addNodeTree( %nodeName ); + if ( %isSelected ) + ShapeEdNodeTreeView.selectItem( ShapeEdNodeTreeView.findItemByName( %nodeName ) ); +} + +function ShapeEdPropWindow::update_onNodeTransformChanged( %this, %nodeName ) +{ + // Default to the selected node if none is specified + if ( %nodeName $= "" ) + { + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id > 0 ) + %nodeName = ShapeEdNodeTreeView.getItemText( %id ); + else + return; + } + + // --- MISC --- + ShapeEdShapeView.updateNodeTransforms(); + if ( ShapeEdNodes-->objectTransform.getValue() ) + GlobalGizmoProfile.setFieldValue(alignment, Object); + else + GlobalGizmoProfile.setFieldValue(alignment, World); + + // --- NODES TAB --- + // Update the node transform fields if necessary + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( ( %id > 0 ) && ( ShapeEdNodeTreeView.getItemText( %id ) $= %nodeName ) ) + { + %isWorld = ShapeEdNodes-->worldTransform.getValue(); + %transform = ShapeEditor.shape.getNodeTransform( %nodeName, %isWorld ); + ShapeEdNodes-->nodePosition.setText( getWords( %transform, 0, 2 ) ); + ShapeEdNodes-->nodeRotation.setText( getWords( %transform, 3, 6 ) ); + } +} + +function ShapeEdNodeTreeView::onClearSelection( %this ) +{ + ShapeEdPropWindow.update_onNodeSelectionChanged( -1 ); +} + +function ShapeEdNodeTreeView::onSelect( %this, %id ) +{ + // Update the node name and transform controls + ShapeEdPropWindow.update_onNodeSelectionChanged( %id ); + + // Update orbit position if orbiting the selected node + if ( ShapeEdShapeView.orbitNode ) + { + %name = %this.getItemText( %id ); + %transform = ShapeEditor.shape.getNodeTransform( %name, 1 ); + ShapeEdShapeView.setOrbitPos( getWords( %transform, 0, 2 ) ); + } +} + +function ShapeEdShapeView::onNodeSelected( %this, %index ) +{ + ShapeEdNodeTreeView.clearSelection(); + if ( %index > 0 ) + { + %name = ShapeEditor.shape.getNodeName( %index ); + %id = ShapeEdNodeTreeView.findItemByName( %name ); + if ( %id > 0 ) + ShapeEdNodeTreeView.selectItem( %id ); + } +} + +function ShapeEdNodes::onAddNode( %this, %name ) +{ + // Add a new node, using the currently selected node as the initial parent + if ( %name $= "" ) + %name = ShapeEditor.getUniqueName( "node", "myNode" ); + + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id <= 0 ) + %parent = ""; + else + %parent = ShapeEdNodeTreeView.getItemText( %id ); + + ShapeEditor.doAddNode( %name, %parent, "0 0 0 0 0 1 0" ); +} + +function ShapeEdNodes::onDeleteNode( %this ) +{ + // Remove the node and all its children from the shape + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id > 0 ) + { + %name = ShapeEdNodeTreeView.getItemText( %id ); + ShapeEditor.doRemoveShapeData( "Node", %name ); + } +} + +// Determine the index of a node in the tree relative to its parent +function ShapeEdNodeTreeView::getChildIndexByName( %this, %name ) +{ + %id = %this.findItemByName( %name ); + %parentId = %this.getParentItem( %id ); + %childId = %this.getChild( %parentId ); + if ( %childId <= 0 ) + return 0; // bad! + + %index = 0; + while ( %childId != %id ) + { + %childId = %this.getNextSibling( %childId ); + %index++; + } + + return %index; +} + +// Add a node and its children to the node tree view +function ShapeEdNodeTreeView::addNodeTree( %this, %nodeName ) +{ + // Abort if already added => something dodgy has happened and we'd end up + // recursing indefinitely + if ( %this.findItemByName( %nodeName ) ) + { + error( "Recursion error in ShapeEdNodeTreeView::addNodeTree" ); + return 0; + } + + // Find parent and add me to it + %parentName = ShapeEditor.shape.getNodeParentName( %nodeName ); + if ( %parentName $= "" ) + %parentName = "<root>"; + + %parentId = %this.findItemByName( %parentName ); + %id = %this.insertItem( %parentId, %nodeName, 0, "" ); + + // Add children + %count = ShapeEditor.shape.getNodeChildCount( %nodeName ); + for ( %i = 0; %i < %count; %i++ ) + %this.addNodeTree( ShapeEditor.shape.getNodeChildName( %nodeName, %i ) ); + + return %id; +} + +function ShapeEdNodes::onEditName( %this ) +{ + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id > 0 ) + { + %oldName = ShapeEdNodeTreeView.getItemText( %id ); + %newName = %this-->nodeName.getText(); + if ( %newName !$= "" ) + ShapeEditor.doRenameNode( %oldName, %newName ); + } +} + +function ShapeEdNodeParentMenu::onSelect( %this, %id, %text ) +{ + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id > 0 ) + { + %name = ShapeEdNodeTreeView.getItemText( %id ); + ShapeEditor.doSetNodeParent( %name, %text ); + } +} + +function ShapeEdNodes::onEditTransform( %this ) +{ + %id = ShapeEdNodeTreeView.getSelectedItem(); + if ( %id > 0 ) + { + %name = ShapeEdNodeTreeView.getItemText( %id ); + + // Get the node transform from the gui + %pos = %this-->nodePosition.getText(); + %rot = %this-->nodeRotation.getText(); + %txfm = %pos SPC %rot; + %isWorld = ShapeEdNodes-->worldTransform.getValue(); + + // Do a quick sanity check to avoid setting wildly invalid transforms + for ( %i = 0; %i < 7; %i++ ) // "x y z aa.x aa.y aa.z aa.angle" + { + if ( getWord( %txfm, %i ) $= "" ) + return; + } + + ShapeEditor.doEditNodeTransform( %name, %txfm, %isWorld, -1 ); + } +} + +function ShapeEdShapeView::onEditNodeTransform( %this, %node, %txfm, %gizmoID ) +{ + ShapeEditor.doEditNodeTransform( %node, %txfm, 1, %gizmoID ); +} + +//------------------------------------------------------------------------------ +// Sequence Editing +//------------------------------------------------------------------------------ + +function ShapeEdPropWindow::onWake( %this ) +{ + ShapeEdTriggerList.triggerId = 1; + + ShapeEdTriggerList.addRow( -1, "-1" TAB "Frame" TAB "Trigger" TAB "State" ); + ShapeEdTriggerList.setRowActive( -1, false ); +} + +function ShapeEdPropWindow::update_onSeqSelectionChanged( %this ) +{ + // Sync the Thread window sequence selection + %row = ShapeEdSequenceList.getSelectedRow(); + if ( ShapeEdThreadWindow-->seqList.getSelectedRow() != ( %row-1 ) ) + { + ShapeEdThreadWindow-->seqList.setSelectedRow( %row-1 ); + return; // selecting a sequence in the Thread window will re-call this function + } + + ShapeEdSeqFromMenu.clear(); + ShapeEdSequences-->blendSeq.clear(); + + // Clear the trigger list + ShapeEdTriggerList.removeAll(); + + // Update the active sequence data + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + // Enable delete button and edit boxes + if ( ShapeEdSeqNodeTabBook.activePage $= "Seq" ) + ShapeEdPropWindow-->deleteBtn.setActive( true ); + ShapeEdSequences-->seqName.setActive( true ); + ShapeEdSequences-->blendFlag.setActive( true ); + ShapeEdSequences-->cyclicFlag.setActive( true ); + ShapeEdSequences-->priority.setActive( true ); + ShapeEdSequences-->addTriggerBtn.setActive( true ); + ShapeEdSequences-->deleteTriggerBtn.setActive( true ); + + // Initialise the sequence properties + %blendData = ShapeEditor.shape.getSequenceBlend( %seqName ); + ShapeEdSequences-->seqName.setText( %seqName ); + ShapeEdSequences-->cyclicFlag.setValue( ShapeEditor.shape.getSequenceCyclic( %seqName ) ); + ShapeEdSequences-->blendFlag.setValue( getField( %blendData, 0 ) ); + ShapeEdSequences-->priority.setText( ShapeEditor.shape.getSequencePriority( %seqName ) ); + + // 'From' and 'Blend' sequence menus + ShapeEdSeqFromMenu.add( "Browse..." ); + %count = ShapeEdSequenceList.rowCount(); + for ( %i = 2; %i < %count; %i++ ) // skip header row and <rootpose> + { + %name = ShapeEdSequenceList.getItemName( %i ); + if ( %name !$= %seqName ) + { + ShapeEdSeqFromMenu.add( %name ); + ShapeEdSequences-->blendSeq.add( %name ); + } + } + ShapeEdSequences-->blendSeq.setText( getField( %blendData, 1 ) ); + ShapeEdSequences-->blendFrame.setText( getField( %blendData, 2 ) ); + + %this.syncPlaybackDetails(); + + // Triggers (must occur after syncPlaybackDetails is called so the slider range is correct) + %count = ShapeEditor.shape.getTriggerCount( %seqName ); + for ( %i = 0; %i < %count; %i++ ) + { + %trigger = ShapeEditor.shape.getTrigger( %seqName, %i ); + ShapeEdTriggerList.addItem( getWord( %trigger, 0 ), getWord( %trigger, 1 ) ); + } + } + else + { + // Disable delete button and edit boxes + if ( ShapeEdSeqNodeTabBook.activePage $= "Seq" ) + ShapeEdPropWindow-->deleteBtn.setActive( false ); + ShapeEdSequences-->seqName.setActive( false ); + ShapeEdSequences-->blendFlag.setActive( false ); + ShapeEdSequences-->cyclicFlag.setActive( false ); + ShapeEdSequences-->priority.setActive( false ); + ShapeEdSequences-->addTriggerBtn.setActive( false ); + ShapeEdSequences-->deleteTriggerBtn.setActive( false ); + + // Clear sequence properties + ShapeEdSequences-->seqName.setText( "" ); + ShapeEdSequences-->cyclicFlag.setValue( 0 ); + ShapeEdSequences-->blendSeq.setText( "" ); + ShapeEdSequences-->blendFlag.setValue( 0 ); + ShapeEdSequences-->priority.setText( 0 ); + + %this.syncPlaybackDetails(); + } + + %this.onTriggerSelectionChanged(); + + ShapeEdSequences-->sequenceListHeader.setExtent( getWord( ShapeEdSequenceList.extent, 0 ) SPC "19" ); + + // Reset current frame + //ShapeEdAnimWindow.setKeyframe( ShapeEdAnimWindow-->seqIn.getText() ); +} + +// Update the GUI in response to a sequence being added +function ShapeEdPropWindow::update_onSequenceAdded( %this, %seqName, %oldIndex ) +{ + // --- MISC --- + ShapeEdSelectWindow.updateHints(); + + // --- SEQUENCES TAB --- + if ( %oldIndex == -1 ) + { + // This is a brand new sequence => add it to the list and make it the + // current selection + %row = ShapeEdSequenceList.insertItem( %seqName, ShapeEdSequenceList.rowCount() ); + ShapeEdSequenceList.scrollVisible( %row ); + ShapeEdSequenceList.setSelectedRow( %row ); + } + else + { + // This sequence has been un-deleted => add it back to the list at the + // original position + ShapeEdSequenceList.insertItem( %seqName, %oldIndex ); + } +} + +function ShapeEdPropWindow::update_onSequenceRemoved( %this, %seqName ) +{ + // --- MISC --- + ShapeEdSelectWindow.updateHints(); + + // --- SEQUENCES TAB --- + %isSelected = ( ShapeEdSequenceList.getSelectedName() $= %seqName ); + ShapeEdSequenceList.removeItem( %seqName ); + if ( %isSelected ) + ShapeEdPropWindow.update_onSeqSelectionChanged(); + + // --- THREADS WINDOW --- + ShapeEdShapeView.refreshThreadSequences(); +} + +function ShapeEdPropWindow::update_onSequenceRenamed( %this, %oldName, %newName ) +{ + // --- MISC --- + ShapeEdSelectWindow.updateHints(); + + // Rename the proxy sequence as well + %oldProxy = ShapeEditor.getProxyName( %oldName ); + %newProxy = ShapeEditor.getProxyName( %newName ); + if ( ShapeEditor.shape.getSequenceIndex( %oldProxy ) != -1 ) + ShapeEditor.shape.renameSequence( %oldProxy, %newProxy ); + + // --- SEQUENCES TAB --- + ShapeEdSequenceList.editColumn( %oldName, 0, %newName ); + if ( ShapeEdSequenceList.getSelectedName() $= %newName ) + ShapeEdSequences-->seqName.setText( %newName ); + + // --- THREADS WINDOW --- + // Update any threads that use this sequence + %active = ShapeEdShapeView.activeThread; + for ( %i = 0; %i < ShapeEdShapeView.getThreadCount(); %i++ ) + { + ShapeEdShapeView.activeThread = %i; + if ( ShapeEdShapeView.getThreadSequence() $= %oldName ) + ShapeEdShapeView.setThreadSequence( %newName, 0, ShapeEdShapeView.threadPos, 0 ); + else if ( ShapeEdShapeView.getThreadSequence() $= %oldProxy ) + ShapeEdShapeView.setThreadSequence( %newProxy, 0, ShapeEdShapeView.threadPos, 0 ); + } + ShapeEdShapeView.activeThread = %active; +} + +function ShapeEdPropWindow::update_onSequenceCyclicChanged( %this, %seqName, %cyclic ) +{ + // --- MISC --- + // Apply the same transformation to the proxy animation if necessary + %proxyName = ShapeEditor.getProxyName( %seqName ); + if ( ShapeEditor.shape.getSequenceIndex( %proxyName ) != -1 ) + ShapeEditor.shape.setSequenceCyclic( %proxyName, %cyclic ); + + // --- SEQUENCES TAB --- + ShapeEdSequenceList.editColumn( %seqName, 1, %cyclic ? "yes" : "no" ); + if ( ShapeEdSequenceList.getSelectedName() $= %seqName ) + ShapeEdSequences-->cyclicFlag.setStateOn( %cyclic ); +} + +function ShapeEdPropWindow::update_onSequenceBlendChanged( %this, %seqName, %blend, + %oldBlendSeq, %oldBlendFrame, %blendSeq, %blendFrame ) +{ + // --- MISC --- + // Apply the same transformation to the proxy animation if necessary + %proxyName = ShapeEditor.getProxyName( %seqName ); + if ( ShapeEditor.shape.getSequenceIndex( %proxyName ) != -1 ) + { + if ( %blend && %oldBlend ) + ShapeEditor.shape.setSequenceBlend( %proxyName, false, %oldBlendSeq, %oldBlendFrame ); + ShapeEditor.shape.setSequenceBlend( %proxyName, %blend, %blendSeq, %blendFrame ); + } + ShapeEdShapeView.updateNodeTransforms(); + + // --- SEQUENCES TAB --- + ShapeEdSequenceList.editColumn( %seqName, 2, %blend ? "yes" : "no" ); + if ( ShapeEdSequenceList.getSelectedName() $= %seqName ) + { + ShapeEdSequences-->blendFlag.setStateOn( %blend ); + ShapeEdSequences-->blendSeq.setText( %blendSeq ); + ShapeEdSequences-->blendFrame.setText( %blendFrame ); + } +} + +function ShapeEdPropWindow::update_onSequencePriorityChanged( %this, %seqName ) +{ + // --- SEQUENCES TAB --- + %priority = ShapeEditor.shape.getSequencePriority( %seqName ); + ShapeEdSequenceList.editColumn( %seqName, 4, %priority ); + if ( ShapeEdSequenceList.getSelectedName() $= %seqName ) + ShapeEdSequences-->priority.setText( %priority ); +} + +function ShapeEdPropWindow::update_onSequenceGroundSpeedChanged( %this, %seqName ) +{ + // nothing to do yet +} + +function ShapeEdPropWindow::syncPlaybackDetails( %this ) +{ + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + // Show sequence in/out bars + ShapeEdAnimWindow-->seqInBar.setVisible( true ); + ShapeEdAnimWindow-->seqOutBar.setVisible( true ); + + // Sync playback controls + %sourceData = ShapeEditor.getSequenceSource( %seqName ); + %seqFrom = rtrim( getFields( %sourceData, 0, 1 ) ); + %seqStart = getField( %sourceData, 2 ); + %seqEnd = getField( %sourceData, 3 ); + %seqFromTotal = getField( %sourceData, 4 ); + + // Display the original source for edited sequences + if ( startswith( %seqFrom, "__backup__" ) ) + { + %backupData = ShapeEditor.getSequenceSource( getField( %seqFrom, 0 ) ); + %seqFrom = rtrim( getFields( %backupData, 0, 1 ) ); + } + + ShapeEdSeqFromMenu.setText( %seqFrom ); + ShapeEdSeqFromMenu.tooltip = ShapeEdSeqFromMenu.getText(); // use tooltip to show long names + ShapeEdSequences-->startFrame.setText( %seqStart ); + ShapeEdSequences-->endFrame.setText( %seqEnd ); + + %val = ShapeEdSeqSlider.getValue() / getWord( ShapeEdSeqSlider.range, 1 ); + ShapeEdSeqSlider.range = "0" SPC ( %seqFromTotal-1 ); + ShapeEdSeqSlider.setValue( %val * getWord( ShapeEdSeqSlider.range, 1 ) ); + ShapeEdThreadSlider.range = ShapeEdSeqSlider.range; + ShapeEdThreadSlider.setValue( ShapeEdSeqSlider.value ); + + ShapeEdAnimWindow.setSequence( %seqName ); + ShapeEdAnimWindow.setPlaybackLimit( "in", %seqStart ); + ShapeEdAnimWindow.setPlaybackLimit( "out", %seqEnd ); + } + else + { + // Hide sequence in/out bars + ShapeEdAnimWindow-->seqInBar.setVisible( false ); + ShapeEdAnimWindow-->seqOutBar.setVisible( false ); + + ShapeEdSeqFromMenu.setText( "" ); + ShapeEdSeqFromMenu.tooltip = ""; + ShapeEdSequences-->startFrame.setText( 0 ); + ShapeEdSequences-->endFrame.setText( 0 ); + + ShapeEdSeqSlider.range = "0 1"; + ShapeEdSeqSlider.setValue( 0 ); + ShapeEdThreadSlider.range = ShapeEdSeqSlider.range; + ShapeEdThreadSlider.setValue( ShapeEdSeqSlider.value ); + ShapeEdAnimWindow.setPlaybackLimit( "in", 0 ); + ShapeEdAnimWindow.setPlaybackLimit( "out", 1 ); + ShapeEdAnimWindow.setSequence( "" ); + } +} + +function ShapeEdSequences::onEditSeqInOut( %this, %type, %val ) +{ + %frameCount = getWord( ShapeEdSeqSlider.range, 1 ); + + // Force value to a frame index within the slider range + %val = mRound( %val ); + if ( %val < 0 ) + %val = 0; + if ( %val > %frameCount ) + %val = %frameCount; + + // Enforce 'in' value must be < 'out' value + if ( %type $= "in" ) + { + if ( %val >= %this-->endFrame.getText() ) + %val = %this-->endFrame.getText() - 1; + %this-->startFrame.setText( %val ); + } + else + { + if ( %val <= %this-->startFrame.getText() ) + %val = %this-->startFrame.getText() + 1; + %this-->endFrame.setText( %val ); + } + + %this.onEditSequenceSource( "" ); +} + +function ShapeEdSequences::onEditSequenceSource( %this, %from ) +{ + // ignore for shapes without sequences + if (ShapeEditor.shape.getSequenceCount() == 0) + return; + + %start = %this-->startFrame.getText(); + %end = %this-->endFrame.getText(); + + if ( ( %start !$= "" ) && ( %end !$= "" ) ) + { + %seqName = ShapeEdSequenceList.getSelectedName(); + %oldSource = ShapeEditor.getSequenceSource( %seqName ); + + if ( %from $= "" ) + %from = rtrim( getFields( %oldSource, 0, 0 ) ); + + if ( getFields( %oldSource, 0, 3 ) !$= ( %from TAB "" TAB %start TAB %end ) ) + ShapeEditor.doEditSeqSource( %seqName, %from, %start, %end ); + } +} + +function ShapeEdSequences::onToggleCyclic( %this ) +{ + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + %cyclic = %this-->cyclicFlag.getValue(); + ShapeEditor.doEditCyclic( %seqName, %cyclic ); + } +} + +function ShapeEdSequences::onEditPriority( %this ) +{ + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + %newPriority = %this-->priority.getText(); + if ( %newPriority !$= "" ) + ShapeEditor.doEditSequencePriority( %seqName, %newPriority ); + } +} + +function ShapeEdSequences::onEditBlend( %this ) +{ + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + // Get the blend flags (current and new) + %oldBlendData = ShapeEditor.shape.getSequenceBlend( %seqName ); + %oldBlend = getField( %oldBlendData, 0 ); + %blend = %this-->blendFlag.getValue(); + + // Ignore changes to the blend reference for non-blend sequences + if ( !%oldBlend && !%blend ) + return; + + // OK - we're trying to change the blend properties of this sequence. The + // new reference sequence and frame must be set. + %blendSeq = %this-->blendSeq.getText(); + %blendFrame = %this-->blendFrame.getText(); + if ( ( %blendSeq $= "" ) || ( %blendFrame $= "" ) ) + { + MessageBoxOK( "Blend reference not set", "The blend reference sequence and " @ + "frame must be set before changing the blend flag or frame." ); + ShapeEdSequences-->blendFlag.setStateOn( %oldBlend ); + return; + } + + // Get the current blend properties (use new values if not specified) + %oldBlendSeq = getField( %oldBlendData, 1 ); + if ( %oldBlendSeq $= "" ) + %oldBlendSeq = %blendSeq; + %oldBlendFrame = getField( %oldBlendData, 2 ); + if ( %oldBlendFrame $= "" ) + %oldBlendFrame = %blendFrame; + + // Check if there is anything to do + if ( ( %oldBlend TAB %oldBlendSeq TAB %oldBlendFrame ) !$= ( %blend TAB %blendSeq TAB %blendFrame ) ) + ShapeEditor.doEditBlend( %seqName, %blend, %blendSeq, %blendFrame ); + } +} + +function ShapeEdSequences::onAddSequence( %this, %name ) +{ + if ( %name $= "" ) + %name = ShapeEditor.getUniqueName( "sequence", "mySequence" ); + + // Use the currently selected sequence as the base + %from = ShapeEdSequenceList.getSelectedName(); + %row = ShapeEdSequenceList.getSelectedRow(); + if ( ( %row < 2 ) && ( ShapeEdSequenceList.rowCount() > 2 ) ) + %row = 2; + if ( %from $= "" ) + { + // No sequence selected => open dialog to browse for one + getLoadFormatFilename( %this @ ".onAddSequenceFromBrowse", ShapeEdFromMenu.lastPath ); + return; + } + else + { + // Add the new sequence + %start = ShapeEdSequences-->startFrame.getText(); + %end = ShapeEdSequences-->endFrame.getText(); + ShapeEditor.doAddSequence( %name, %from, %start, %end ); + } +} + +function ShapeEdSequences::onAddSequenceFromBrowse( %this, %path ) +{ + // Add a new sequence from the browse path + %path = makeRelativePath( %path, getMainDotCSDir() ); + ShapeEdFromMenu.lastPath = %path; + + %name = ShapeEditor.getUniqueName( "sequence", "mySequence" ); + ShapeEditor.doAddSequence( %name, %path, 0, -1 ); +} + +// Delete the selected sequence +function ShapeEdSequences::onDeleteSequence( %this ) +{ + %row = ShapeEdSequenceList.getSelectedRow(); + if ( %row != -1 ) + { + %seqName = ShapeEdSequenceList.getItemName( %row ); + ShapeEditor.doRemoveShapeData( "Sequence", %seqName ); + } +} + +// Get the name of the currently selected sequence +function ShapeEdSequenceList::getSelectedName( %this ) +{ + %row = %this.getSelectedRow(); + return ( %row > 1 ) ? %this.getItemName( %row ) : ""; // ignore header row +} + +// Get the sequence name from the indexed row +function ShapeEdSequenceList::getItemName( %this, %row ) +{ + return getField( %this.getRowText( %row ), 0 ); +} + +// Get the index in the list of the sequence with the given name +function ShapeEdSequenceList::getItemIndex( %this, %name ) +{ + for ( %i = 1; %i < %this.rowCount(); %i++ ) // ignore header row + { + if ( %this.getItemName( %i ) $= %name ) + return %i; + } + return -1; +} + +// Change one of the fields in the sequence list +function ShapeEdSequenceList::editColumn( %this, %name, %col, %text ) +{ + %row = %this.getItemIndex( %name ); + %rowText = setField( %this.getRowText( %row ), %col, %text ); + + // Update the Properties and Thread sequence lists + %id = %this.getRowId( %row ); + if ( %col == 0 ) + ShapeEdThreadWindow-->seqList.setRowById( %id, %text ); // Sync name in Thread window + %this.setRowById( %id, %rowText ); +} + +function ShapeEdSequenceList::addItem( %this, %name ) +{ + return %this.insertItem( %name, %this.rowCount() ); +} + +function ShapeEdSequenceList::insertItem( %this, %name, %index ) +{ + %cyclic = ShapeEditor.shape.getSequenceCyclic( %name ) ? "yes" : "no"; + %blend = getField( ShapeEditor.shape.getSequenceBlend( %name ), 0 ) ? "yes" : "no"; + %frameCount = ShapeEditor.shape.getSequenceFrameCount( %name ); + %priority = ShapeEditor.shape.getSequencePriority( %name ); + + // Add the item to the Properties and Thread sequence lists + %this.seqId++; // use this to keep the row IDs synchronised + ShapeEdThreadWindow-->seqList.addRow( %this.seqId, %name, %index-1 ); // no header row + return %this.addRow( %this.seqId, %name TAB %cyclic TAB %blend TAB %frameCount TAB %priority, %index ); +} + +function ShapeEdSequenceList::removeItem( %this, %name ) +{ + %index = %this.getItemIndex( %name ); + if ( %index >= 0 ) + { + %this.removeRow( %index ); + ShapeEdThreadWindow-->seqList.removeRow( %index-1 ); // no header row + } +} + +function ShapeEdSeqFromMenu::onSelect( %this, %id, %text ) +{ + if ( %text $= "Browse..." ) + { + // Reset menu text + %seqName = ShapeEdSequenceList.getSelectedName(); + %seqFrom = rtrim( getFields( ShapeEditor.getSequenceSource( %seqName ), 0, 1 ) ); + %this.setText( %seqFrom ); + + // Allow the user to browse for an external source of animation data + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); + } + else + { + ShapeEdSequences.onEditSequenceSource( %text ); + } +} + +function ShapeEdSeqFromMenu::onBrowseSelect( %this, %path ) +{ + %path = makeRelativePath( %path, getMainDotCSDir() ); + %this.lastPath = %path; + %this.setText( %path ); + ShapeEdSequences.onEditSequenceSource( %path ); +} + +//------------------------------------------------------------------------------ +// Threads and Animation +//------------------------------------------------------------------------------ + +function ShapeEdThreadWindow::onWake( %this ) +{ + %this-->useTransitions.setValue( 1 ); + %this-->transitionTime.setText( "0.5" ); + + %this-->transitionTo.clear(); + %this-->transitionTo.add( "synched position", 0 ); + %this-->transitionTo.add( "slider position", 1 ); + %this-->transitionTo.setSelected( 0 ); + + %this-->transitionTarget.clear(); + %this-->transitionTarget.add( "plays during transition", 0 ); + %this-->transitionTarget.add( "pauses during transition", 1 ); + %this-->transitionTarget.setSelected( 0 ); +} + +// Update the GUI in response to the shape selection changing +function ShapeEdThreadWindow::update_onShapeSelectionChanged( %this ) +{ + ShapeEdThreadList.clear(); + %this-->seqList.clear(); + %this-->seqList.addRow( 0, "<rootpose>" ); +} + +function ShapeEdAnimWIndow::threadPosToKeyframe( %this, %pos ) +{ + if ( %this.usingProxySeq ) + { + %start = getWord( ShapeEdSeqSlider.range, 0 ); + %end = getWord( ShapeEdSeqSlider.range, 1 ); + } + else + { + %start = ShapeEdAnimWindow.seqStartFrame; + %end = ShapeEdAnimWindow.seqEndFrame; + } + + return %start + ( %end - %start ) * %pos; +} + +function ShapeEdAnimWindow::keyframeToThreadPos( %this, %frame ) +{ + if ( %this.usingProxySeq ) + { + %start = getWord( ShapeEdSeqSlider.range, 0 ); + %end = getWord( ShapeEdSeqSlider.range, 1 ); + } + else + { + %start = ShapeEdAnimWindow.seqStartFrame; + %end = ShapeEdAnimWindow.seqEndFrame; + } + + return ( %frame - %start ) / ( %end - %start ); +} + +function ShapeEdAnimWindow::setKeyframe( %this, %frame ) +{ + ShapeEdSeqSlider.setValue( %frame ); + if ( ShapeEdThreadWindow-->transitionTo.getText() $= "synched position" ) + ShapeEdThreadSlider.setValue( %frame ); + + // Update the position of the active thread => if outside the in/out range, + // need to switch to the proxy sequence + if ( !%this.usingProxySeq ) + { + if ( ( %frame < %this.seqStartFrame ) || ( %frame > %this.seqEndFrame) ) + { + %this.usingProxySeq = true; + %proxyName = ShapeEditor.getProxyName( ShapeEdShapeView.getThreadSequence() ); + ShapeEdShapeView.setThreadSequence( %proxyName, 0, 0, false ); + } + } + + ShapeEdShapeView.threadPos = %this.keyframeToThreadPos( %frame ); +} + +function ShapeEdAnimWindow::setNoProxySequence( %this ) +{ + // no need to use the proxy sequence during playback + if ( %this.usingProxySeq ) + { + %this.usingProxySeq = false; + %seqName = ShapeEditor.getUnproxyName( ShapeEdShapeView.getThreadSequence() ); + ShapeEdShapeView.setThreadSequence( %seqName, 0, 0, false ); + ShapeEdShapeView.threadPos = %this.keyframeToThreadPos( ShapeEdSeqSlider.getValue() ); + } +} + +function ShapeEdAnimWindow::togglePause( %this ) +{ + if ( %this-->pauseBtn.getValue() == 0 ) + { + %this.lastDirBkwd = %this-->playBkwdBtn.getValue(); + %this-->pauseBtn.performClick(); + } + else + { + %this.setNoProxySequence(); + if ( %this.lastDirBkwd ) + %this-->playBkwdBtn.performClick(); + else + %this-->playFwdBtn.performClick(); + } +} + +function ShapeEdAnimWindow::togglePingPong( %this ) +{ + ShapeEdShapeView.threadPingPong = %this-->pingpong.getValue(); + if ( %this-->playFwdBtn.getValue() ) + %this-->playFwdBtn.performClick(); + else if ( %this-->playBkwdBtn.getValue() ) + %this-->playBkwdBtn.performClick(); +} + +function ShapeEdSeqSlider::onMouseDragged( %this ) +{ + // Pause the active thread when the slider is dragged + if ( ShapeEdAnimWindow-->pauseBtn.getValue() == 0 ) + ShapeEdAnimWindow-->pauseBtn.performClick(); + + ShapeEdAnimWindow.setKeyframe( %this.getValue() ); +} + +function ShapeEdThreadSlider::onMouseDragged( %this ) +{ + if ( ShapeEdThreadWindow-->transitionTo.getText() $= "synched position" ) + { + // Pause the active thread when the slider is dragged + if ( ShapeEdAnimWindow-->pauseBtn.getValue() == 0 ) + ShapeEdAnimWindow-->pauseBtn.performClick(); + + ShapeEdAnimWindow.setKeyframe( %this.getValue() ); + } +} + +function ShapeEdShapeView::onThreadPosChanged( %this, %pos, %inTransition ) +{ + // Update sliders + %frame = ShapeEdAnimWindow.threadPosToKeyframe( %pos ); + ShapeEdSeqSlider.setValue( %frame ); + + if ( ShapeEdThreadWindow-->transitionTo.getText() $= "synched position" ) + { + ShapeEdThreadSlider.setValue( %frame ); + + // Highlight the slider during transitions + if ( %inTransition ) + ShapeEdThreadSlider.profile = GuiShapeEdTransitionSliderProfile; + else + ShapeEdThreadSlider.profile = ToolsGuiSliderProfile; + } +} + +// Set the direction of the current thread (-1: reverse, 0: paused, 1: forward) +function ShapeEdAnimWindow::setThreadDirection( %this, %dir ) +{ + // Update thread direction + ShapeEdShapeView.threadDirection = %dir; + + // Sync the controls in the thread window + switch ( %dir ) + { + case -1: ShapeEdThreadWindow-->playBkwdBtn.setStateOn( 1 ); + case 0: ShapeEdThreadWindow-->pauseBtn.setStateOn( 1 ); + case 1: ShapeEdThreadWindow-->playFwdBtn.setStateOn( 1 ); + } +} + +// Set the sequence to play +function ShapeEdAnimWindow::setSequence( %this, %seqName ) +{ + %this.usingProxySeq = false; + + if ( ShapeEdThreadWindow-->useTransitions.getValue() ) + { + %transTime = ShapeEdThreadWindow-->transitionTime.getText(); + if ( ShapeEdThreadWindow-->transitionTo.getText() $= "synched position" ) + %transPos = -1; + else + %transPos = %this.keyframeToThreadPos( ShapeEdThreadSlider.getValue() ); + %transPlay = ( ShapeEdThreadWindow-->transitionTarget.getText() $= "plays during transition" ); + } + else + { + %transTime = 0; + %transPos = 0; + %transPlay = 0; + } + + // No transition when sequence is not changing + if ( %seqName $= ShapeEdShapeView.getThreadSequence() ) + %transTime = 0; + + if ( %seqName !$= "" ) + { + // To be able to effectively scrub through the animation, we need to have all + // frames available, even if it was added with only a subset. If that is the + // case, then create a proxy sequence that has all the frames instead. + %sourceData = ShapeEditor.getSequenceSource( %seqName ); + %from = rtrim( getFields( %sourceData, 0, 1 ) ); + %startFrame = getField( %sourceData, 2 ); + %endFrame = getField( %sourceData, 3 ); + %frameCount = getField( %sourceData, 4 ); + + if ( ( %startFrame != 0 ) || ( %endFrame != ( %frameCount-1 ) ) ) + { + %proxyName = ShapeEditor.getProxyName( %seqName ); + if ( ShapeEditor.shape.getSequenceIndex( %proxyName ) != -1 ) + { + ShapeEditor.shape.removeSequence( %proxyName ); + ShapeEdShapeView.refreshThreadSequences(); + } + ShapeEditor.shape.addSequence( %from, %proxyName ); + + // Limit the transition position to the in/out range + %transPos = mClamp( %transPos, 0, 1 ); + } + } + + ShapeEdShapeView.setThreadSequence( %seqName, %transTime, %transPos, %transPlay ); +} + +function ShapeEdAnimWindow::getTimelineBitmapPos( %this, %val, %width ) +{ + %frameCount = getWord( ShapeEdSeqSlider.range, 1 ); + %pos_x = getWord( ShapeEdSeqSlider.getPosition(), 0 ); + %len_x = getWord( ShapeEdSeqSlider.getExtent(), 0 ) - %width; + return %pos_x + ( ( %len_x * %val / %frameCount ) ); +} + +// Set the in or out sequence limit +function ShapeEdAnimWindow::setPlaybackLimit( %this, %limit, %val ) +{ + // Determine where to place the in/out bar on the slider + %thumbWidth = 8; // width of the thumb bitmap + %pos_x = %this.getTimelineBitmapPos( %val, %thumbWidth ); + + if ( %limit $= "in" ) + { + %this.seqStartFrame = %val; + %this-->seqIn.setText( %val ); + %this-->seqInBar.setPosition( %pos_x, 0 ); + } + else + { + %this.seqEndFrame = %val; + %this-->seqOut.setText( %val ); + %this-->seqOutBar.setPosition( %pos_x, 0 ); + } +} + +function ShapeEdThreadWindow::onAddThread( %this ) +{ + ShapeEdShapeView.addThread(); + ShapeEdThreadList.addRow( %this.threadID++, ShapeEdThreadList.rowCount() ); + ShapeEdThreadList.setSelectedRow( ShapeEdThreadList.rowCount()-1 ); +} + +function ShapeEdThreadWindow::onRemoveThread( %this ) +{ + if ( ShapeEdThreadList.rowCount() > 1 ) + { + // Remove the selected thread + %row = ShapeEdThreadList.getSelectedRow(); + ShapeEdShapeView.removeThread( %row ); + ShapeEdThreadList.removeRow( %row ); + + // Update list (threads are always numbered 0-N) + %rowCount = ShapeEdThreadList.rowCount(); + for ( %i = %row; %i < %rowCount; %i++ ) + ShapeEdThreadList.setRowById( ShapeEdThreadList.getRowId( %i ), %i ); + + // Select the next thread + if ( %row >= %rowCount ) + %row = %rowCount - 1; + + ShapeEdThreadList.setSelectedRow( %row ); + } +} + +function ShapeEdThreadList::onSelect( %this, %row, %text ) +{ + ShapeEdShapeView.activeThread = ShapeEdThreadList.getSelectedRow(); + + // Select the active thread's sequence in the list + %seqName = ShapeEdShapeView.getThreadSequence(); + if ( %seqName $= "" ) + %seqName = "<rootpose>"; + else if ( startswith( %seqName, "__proxy__" ) ) + %seqName = ShapeEditor.getUnproxyName( %seqName ); + + %seqIndex = ShapeEdSequenceList.getItemIndex( %seqName ); + ShapeEdSequenceList.setSelectedRow( %seqIndex ); + + // Update the playback controls + switch ( ShapeEdShapeView.threadDirection ) + { + case -1: ShapeEdAnimWindow-->playBkwdBtn.performClick(); + case 0: ShapeEdAnimWindow-->pauseBtn.performClick(); + case 1: ShapeEdAnimWindow-->playFwdBtn.performClick(); + } + SetToggleButtonValue( ShapeEdAnimWindow-->pingpong, ShapeEdShapeView.threadPingPong ); +} + +//------------------------------------------------------------------------------ +// Trigger Editing +//------------------------------------------------------------------------------ + +function ShapeEdPropWindow::onTriggerSelectionChanged( %this ) +{ + %row = ShapeEdTriggerList.getSelectedRow(); + if ( %row > 0 ) // skip header row + { + %text = ShapeEdTriggerList.getRowText( %row ); + + ShapeEdSequences-->triggerFrame.setActive( true ); + ShapeEdSequences-->triggerNum.setActive( true ); + ShapeEdSequences-->triggerOnOff.setActive( true ); + + ShapeEdSequences-->triggerFrame.setText( getField( %text, 1 ) ); + ShapeEdSequences-->triggerNum.setText( getField( %text, 2 ) ); + ShapeEdSequences-->triggerOnOff.setValue( getField( %text, 3 ) $= "on" ); + } + else + { + // No trigger selected + ShapeEdSequences-->triggerFrame.setActive( false ); + ShapeEdSequences-->triggerNum.setActive( false ); + ShapeEdSequences-->triggerOnOff.setActive( false ); + + ShapeEdSequences-->triggerFrame.setText( "" ); + ShapeEdSequences-->triggerNum.setText( "" ); + ShapeEdSequences-->triggerOnOff.setValue( 0 ); + } +} + +function ShapeEdSequences::onEditName( %this ) +{ + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + %newName = %this-->seqName.getText(); + if ( %newName !$= "" ) + ShapeEditor.doRenameSequence( %seqName, %newName ); + } +} + +function ShapeEdPropWindow::update_onTriggerAdded( %this, %seqName, %frame, %state ) +{ + // --- SEQUENCES TAB --- + // Add trigger to list if this sequence is selected + if ( ShapeEdSequenceList.getSelectedName() $= %seqName ) + ShapeEdTriggerList.addItem( %frame, %state ); +} + +function ShapeEdPropWindow::update_onTriggerRemoved( %this, %seqName, %frame, %state ) +{ + // --- SEQUENCES TAB --- + // Remove trigger from list if this sequence is selected + if ( ShapeEdSequenceList.getSelectedName() $= %seqName ) + ShapeEdTriggerList.removeItem( %frame, %state ); +} + +function ShapeEdTriggerList::getTriggerText( %this, %frame, %state ) +{ + // First column is invisible and used only for sorting + %sortKey = ( %frame * 1000 ) + ( mAbs( %state ) * 10 ) + ( ( %state > 0 ) ? 1 : 0 ); + return %sortKey TAB %frame TAB mAbs( %state ) TAB ( ( %state > 0 ) ? "on" : "off" ); +} + +function ShapeEdTriggerList::addItem( %this, %frame, %state ) +{ + // Add to text list + %row = %this.addRow( %this.triggerId, %this.getTriggerText( %frame, %state ) ); + %this.sortNumerical( 0, true ); + + // Add marker to animation timeline + %pos = ShapeEdAnimWindow.getTimelineBitmapPos( ShapeEdAnimWindow-->seqIn.getText() + %frame, 2 ); + %ctrl = new GuiBitmapCtrl() + { + internalName = "trigger" @ %this.triggerId; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = %pos SPC "0"; + Extent = "2 12"; + bitmap = "tools/shapeEditor/images/trigger_marker"; + }; + ShapeEdAnimWindow.getObject(0).addGuiControl( %ctrl ); + %this.triggerId++; +} + +function ShapeEdTriggerList::removeItem( %this, %frame, %state ) +{ + // Remove from text list + %row = %this.findTextIndex( %this.getTriggerText( %frame, %state ) ); + if ( %row > 0 ) + { + eval( "ShapeEdAnimWindow-->trigger" @ %this.getRowId( %row ) @ ".delete();" ); + %this.removeRow( %row ); + } +} + +function ShapeEdTriggerList::removeAll( %this ) +{ + %count = %this.rowCount(); + for ( %row = %count-1; %row > 0; %row-- ) + { + eval( "ShapeEdAnimWindow-->trigger" @ %this.getRowId( %row ) @ ".delete();" ); + %this.removeRow( %row ); + } +} + +function ShapeEdTriggerList::updateItem( %this, %oldFrame, %oldState, %frame, %state ) +{ + // Update text list entry + %oldText = %this.getTriggerText( %oldFrame, %oldState ); + %row = %this.getSelectedRow(); + if ( ( %row <= 0 ) || ( %this.getRowText( %row ) !$= %oldText ) ) + %row = %this.findTextIndex( %oldText ); + if ( %row > 0 ) + { + %updatedId = %this.getRowId( %row ); + %newText = %this.getTriggerText( %frame, %state ); + %this.setRowById( %updatedId, %newText ); + + // keep selected row the same + %selectedId = %this.getSelectedId(); + %this.sortNumerical( 0, true ); + %this.setSelectedById( %selectedId ); + + // Update animation timeline marker + if ( %frame != %oldFrame ) + { + %pos = ShapeEdAnimWindow.getTimelineBitmapPos( ShapeEdAnimWindow-->seqIn.getText() + %frame, 2 ); + eval( "%ctrl = ShapeEdAnimWindow-->trigger" @ %updatedId @ ";" ); + %ctrl.position = %pos SPC "0"; + } + } +} + +function ShapeEdSequences::onAddTrigger( %this ) +{ + // Can only add triggers if a sequence is selected + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + // Add a new trigger at the current frame + %frame = mRound( ShapeEdSeqSlider.getValue() ) - %this-->startFrame.getText(); + if ((%frame < 0) || (%frame > %this-->endFrame.getText() - %this-->startFrame.getText())) + { + MessageBoxOK( "Error", "Trigger out of range of the selected animation." ); + } + else + { + %state = ShapeEdTriggerList.rowCount() % 30; + ShapeEditor.doAddTrigger( %seqName, %frame, %state ); + } + } +} + +function ShapeEdTriggerList::onDeleteSelection( %this ) +{ + // Can only delete a trigger if a sequence and trigger are selected + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + %row = %this.getSelectedRow(); + if ( %row > 0 ) + { + %text = %this.getRowText( %row ); + %frame = getWord( %text, 1 ); + %state = getWord( %text, 2 ); + %state *= ( getWord( %text, 3 ) $= "on" ) ? 1 : -1; + ShapeEditor.doRemoveTrigger( %seqName, %frame, %state ); + } + } +} + +function ShapeEdTriggerList::onEditSelection( %this ) +{ + // Can only edit triggers if a sequence and trigger are selected + %seqName = ShapeEdSequenceList.getSelectedName(); + if ( %seqName !$= "" ) + { + %row = ShapeEdTriggerList.getSelectedRow(); + if ( %row > 0 ) + { + %text = %this.getRowText( %row ); + %oldFrame = getWord( %text, 1 ); + %oldState = getWord( %text, 2 ); + %oldState *= ( getWord( %text, 3 ) $= "on" ) ? 1 : -1; + + %frame = mRound( ShapeEdSequences-->triggerFrame.getText() ); + %state = mRound( mAbs( ShapeEdSequences-->triggerNum.getText() ) ); + %state *= ShapeEdSequences-->triggerOnOff.getValue() ? 1 : -1; + + if ( ( %frame >= 0 ) && ( %state != 0 ) ) + ShapeEditor.doEditTrigger( %seqName, %oldFrame, %oldState, %frame, %state ); + } + } +} + +//------------------------------------------------------------------------------ +// Material Editing +//------------------------------------------------------------------------------ + +function ShapeEdMaterials::updateMaterialList( %this ) +{ + // --- MATERIALS TAB --- + ShapeEdMaterialList.clear(); + ShapeEdMaterialList.addRow( -2, "Name" TAB "Mapped" ); + ShapeEdMaterialList.setRowActive( -2, false ); + ShapeEdMaterialList.addRow( -1, "<none>" ); + %count = ShapeEditor.shape.getTargetCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %matName = ShapeEditor.shape.getTargetName( %i ); + %mapped = getMaterialMapping( %matName ); + if ( %mapped $= "" ) + ShapeEdMaterialList.addRow( WarningMaterial.getID(), %matName TAB "unmapped" ); + else + ShapeEdMaterialList.addRow( %mapped.getID(), %matName TAB %mapped ); + } + + ShapeEdMaterials-->materialListHeader.setExtent( getWord( ShapeEdMaterialList.extent, 0 ) SPC "19" ); +} + +function ShapeEdMaterials::updateSelectedMaterial( %this, %highlight ) +{ + // Remove the highlight effect from the old selection + if ( isObject( %this.selectedMaterial ) ) + { + %this.selectedMaterial.diffuseMap[1] = %this.savedMap; + %this.selectedMaterial.reload(); + } + + // Apply the highlight effect to the new selected material + %this.selectedMapTo = getField( ShapeEdMaterialList.getRowText( ShapeEdMaterialList.getSelectedRow() ), 0 ); + %this.selectedMaterial = ShapeEdMaterialList.getSelectedId(); + %this.savedMap = %this.selectedMaterial.diffuseMap[1]; + if ( %highlight && isObject( %this.selectedMaterial ) ) + { + %this.selectedMaterial.diffuseMap[1] = "tools/shapeEditor/images/highlight_material"; + %this.selectedMaterial.reload(); + } +} + +function ShapeEdMaterials::editSelectedMaterial( %this ) +{ + if ( isObject( %this.selectedMaterial ) ) + { + // Remove the highlight effect from the selected material, then switch + // to the Material Editor + %this.updateSelectedMaterial( false ); + + // Create a temporary TSStatic so the MaterialEditor can query the model's + // materials. + pushInstantGroup(); + %this.tempShape = new TSStatic() { + shapeName = ShapeEditor.shape.baseShape; + collisionType = "None"; + }; + popInstantGroup(); + + MaterialEditorGui.currentMaterial = %this.selectedMaterial; + MaterialEditorGui.currentObject = $Tools::materialEditorList = %this.tempShape; + + ShapeEdSelectWindow.setVisible( false ); + ShapeEdPropWindow.setVisible( false ); + + EditorGui-->MatEdPropertiesWindow.setVisible( true ); + EditorGui-->MatEdPreviewWindow.setVisible( true ); + + MatEd_phoBreadcrumb.setVisible( true ); + MatEd_phoBreadcrumb.command = "ShapeEdMaterials.editSelectedMaterialEnd();"; + + advancedTextureMapsRollout.Expanded = false; + materialAnimationPropertiesRollout.Expanded = false; + materialAdvancedPropertiesRollout.Expanded = false; + + MaterialEditorGui.open(); + MaterialEditorGui.setActiveMaterial( %this.selectedMaterial ); + + %id = SubMaterialSelector.findText( %this.selectedMapTo ); + if( %id != -1 ) + SubMaterialSelector.setSelected( %id ); + } +} + +function ShapeEdMaterials::editSelectedMaterialEnd( %this, %closeEditor ) +{ + MatEd_phoBreadcrumb.setVisible( false ); + MatEd_phoBreadcrumb.command = ""; + + MaterialEditorGui.quit(); + EditorGui-->MatEdPropertiesWindow.setVisible( false ); + EditorGui-->MatEdPreviewWindow.setVisible( false ); + + // Delete the temporary TSStatic + %this.tempShape.delete(); + + if( !%closeEditor ) + { + ShapeEdSelectWindow.setVisible( true ); + ShapeEdPropWindow.setVisible( true ); + } +} + +//------------------------------------------------------------------------------ +// Detail/Mesh Editing +//------------------------------------------------------------------------------ + +function ShapeEdDetails::onWake( %this ) +{ + // Initialise popup menus + %this-->bbType.clear(); + %this-->bbType.add( "None", 0 ); + %this-->bbType.add( "Billboard", 1 ); + %this-->bbType.add( "Z Billboard", 2 ); + + %this-->addGeomTo.clear(); + %this-->addGeomTo.add( "current detail", 0 ); + %this-->addGeomTo.add( "new detail", 1 ); + %this-->addGeomTo.setSelected( 0, false ); + + ShapeEdDetailTree.onDefineIcons(); +} + +function ShapeEdDetailTree::onDefineIcons(%this) +{ + // Set the tree view icon indices and texture paths + %this._imageNone = 0; + %this._imageHidden = 1; + + %icons = ":" @ // no icon + "tools/gui/images/visible_i:"; // hidden + + %this.buildIconTable( %icons ); +} + +// Return true if the item in the details tree view is a detail level (false if +// a mesh) +function ShapeEdDetailTree::isDetailItem( %this, %id ) +{ + return ( %this.getParentItem( %id ) == 1 ); +} + +// Get the detail level index from the ID of an item in the details tree view +function ShapeEdDetailTree::getDetailLevelFromItem( %this, %id ) +{ + if ( %this.isDetailItem( %id ) ) + %detSize = %this.getItemValue( %id ); + + else + %detSize = %this.getItemValue( %this.getParentItem( %id ) ); + return ShapeEditor.shape.getDetailLevelIndex( %detSize ); +} + +function ShapeEdDetailTree::addMeshEntry( %this, %name, %noSync ) +{ + // Add new detail level if required + %size = getTrailingNumber( %name ); + %detailID = %this.findItemByValue( %size ); + if ( %detailID <= 0 ) + { + %dl = ShapeEditor.shape.getDetailLevelIndex( %size ); + %detName = ShapeEditor.shape.getDetailLevelName( %dl ); + %detailID = ShapeEdDetailTree.insertItem( 1, %detName, %size, "" ); + + // Sort details by decreasing size + for ( %sibling = ShapeEdDetailTree.getPrevSibling( %detailID ); + ( %sibling > 0 ) && ( ShapeEdDetailTree.getItemValue( %sibling ) < %size ); + %sibling = ShapeEdDetailTree.getPrevSibling( %detailID ) ) + ShapeEdDetailTree.moveItemUp( %detailID ); + + if ( !%noSync ) + ShapeEdDetails.update_onDetailsChanged(); + } + return %this.insertItem( %detailID, %name, "", "" ); +} + +function ShapeEdDetailTree::removeMeshEntry( %this, %name, %size ) +{ + %size = getTrailingNumber( %name ); + %id = ShapeEdDetailTree.findItemByName( %name ); + if ( ShapeEditor.shape.getDetailLevelIndex( %size ) < 0 ) + { + // Last mesh of a detail level has been removed => remove the detail level + %this.removeItem( %this.getParentItem( %id ) ); + ShapeEdDetails.update_onDetailsChanged(); + } + else + %this.removeItem( %id ); +} + +function ShapeEdAdvancedWindow::update_onShapeSelectionChanged( %this ) +{ + ShapeEdShapeView.currentDL = 0; + ShapeEdShapeView.onDetailChanged(); +} + +function ShapeEdPropWindow::update_onDetailRenamed( %this, %oldName, %newName ) +{ + // --- DETAILS TAB --- + // Rename detail entry + %id = ShapeEdDetailTree.findItemByName( %oldName ); + if ( %id > 0 ) + { + %size = ShapeEdDetailTree.getItemValue( %id ); + ShapeEdDetailTree.editItem( %id, %newName, %size ); + + // Sync text if item is selected + if ( ShapeEdDetailTree.isItemSelected( %id ) && + ( ShapeEdDetails-->meshName.getText() !$= %newName ) ) + ShapeEdDetails-->meshName.setText( stripTrailingNumber( %newName ) ); + } +} + +function ShapeEdPropWindow::update_onDetailSizeChanged( %this, %oldSize, %newSize ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + %dl = ShapeEditor.shape.getDetailLevelIndex( %newSize ); + if ( ShapeEdAdvancedWindow-->detailSize.getText() $= %oldSize ) + { + ShapeEdShapeView.currentDL = %dl; + ShapeEdAdvancedWindow-->detailSize.setText( %newSize ); + ShapeEdDetails-->meshSize.setText( %newSize ); + } + + // --- DETAILS TAB --- + // Update detail entry then resort details by size + %id = ShapeEdDetailTree.findItemByValue( %oldSize ); + %detName = ShapeEditor.shape.getDetailLevelName( %dl ); + ShapeEdDetailTree.editItem( %id, %detName, %newSize ); + + for ( %sibling = ShapeEdDetailTree.getPrevSibling( %id ); + ( %sibling > 0 ) && ( ShapeEdDetailTree.getItemValue( %sibling ) < %newSize ); + %sibling = ShapeEdDetailTree.getPrevSibling( %id ) ) + ShapeEdDetailTree.moveItemUp( %id ); + for ( %sibling = ShapeEdDetailTree.getNextSibling( %id ); + ( %sibling > 0 ) && ( ShapeEdDetailTree.getItemValue( %sibling ) > %newSize ); + %sibling = ShapeEdDetailTree.getNextSibling( %id ) ) + ShapeEdDetailTree.moveItemDown( %id ); + + // Update size values for meshes of this detail + for ( %child = ShapeEdDetailTree.getChild( %id ); + %child > 0; + %child = ShapeEdDetailTree.getNextSibling( %child ) ) + { + %meshName = stripTrailingNumber( ShapeEdDetailTree.getItemText( %child ) ); + ShapeEdDetailTree.editItem( %child, %meshName SPC %newSize, "" ); + } +} + +function ShapeEdDetails::update_onDetailsChanged( %this ) +{ + %detailCount = ShapeEditor.shape.getDetailLevelCount(); + ShapeEdAdvancedWindow-->detailSlider.range = "0" SPC ( %detailCount-1 ); + if ( %detailCount >= 2 ) + ShapeEdAdvancedWindow-->detailSlider.ticks = %detailCount - 2; + else + ShapeEdAdvancedWindow-->detailSlider.ticks = 0; + + // Initialise imposter settings + ShapeEdAdvancedWindow-->bbUseImposters.setValue( ShapeEditor.shape.getImposterDetailLevel() != -1 ); + + // Update detail parameters + if ( ShapeEdShapeView.currentDL < %detailCount ) + { + %settings = ShapeEditor.shape.getImposterSettings( ShapeEdShapeView.currentDL ); + %isImposter = getWord( %settings, 0 ); + + ShapeEdAdvancedWindow-->imposterInactive.setVisible( !%isImposter ); + + ShapeEdAdvancedWindow-->bbEquatorSteps.setText( getField( %settings, 1 ) ); + ShapeEdAdvancedWindow-->bbPolarSteps.setText( getField( %settings, 2 ) ); + ShapeEdAdvancedWindow-->bbDetailLevel.setText( getField( %settings, 3 ) ); + ShapeEdAdvancedWindow-->bbDimension.setText( getField( %settings, 4 ) ); + ShapeEdAdvancedWindow-->bbIncludePoles.setValue( getField( %settings, 5 ) ); + ShapeEdAdvancedWindow-->bbPolarAngle.setText( getField( %settings, 6 ) ); + } +} + +function ShapeEdPropWindow::update_onObjectNodeChanged( %this, %objName ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + + // --- DETAILS TAB --- + // Update the node popup menu if this object is selected + if ( ShapeEdDetails-->meshName.getText() $= %objName ) + { + %nodeName = ShapeEditor.shape.getObjectNode( %objName ); + if ( %nodeName $= "" ) + %nodeName = "<root>"; + %id = ShapeEdDetails-->objectNode.findText( %nodeName ); + ShapeEdDetails-->objectNode.setSelected( %id, false ); + } +} + +function ShapeEdPropWindow::update_onObjectRenamed( %this, %oldName, %newName ) +{ + // --- DETAILS TAB --- + // Rename tree entries for this object + %count = ShapeEditor.shape.getMeshCount( %newName ); + for ( %i = 0; %i < %count; %i++ ) + { + %size = getTrailingNumber( ShapeEditor.shape.getMeshName( %newName, %i ) ); + %id = ShapeEdDetailTree.findItemByName( %oldName SPC %size ); + if ( %id > 0 ) + { + ShapeEdDetailTree.editItem( %id, %newName SPC %size, "" ); + + // Sync text if item is selected + if ( ShapeEdDetailTree.isItemSelected( %id ) && + ( ShapeEdDetails-->meshName.getText() !$= %newName ) ) + ShapeEdDetails-->meshName.setText( %newName ); + } + } +} + +function ShapeEdPropWindow::update_onMeshAdded( %this, %meshName ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.updateNodeTransforms(); + + // --- COLLISION WINDOW --- + // Add object to target list if it does not already exist + if ( !ShapeEditor.isCollisionMesh( %meshName ) ) + { + %objName = stripTrailingNumber( %meshName ); + %id = ShapeEdColWindow-->colTarget.findText( %objName ); + if ( %id == -1 ) + ShapeEdColWindow-->colTarget.add( %objName ); + } + + // --- DETAILS TAB --- + %id = ShapeEdDetailTree.addMeshEntry( %meshName ); + ShapeEdDetailTree.clearSelection(); + ShapeEdDetailTree.selectItem( %id ); +} + +function ShapeEdPropWindow::update_onMeshSizeChanged( %this, %meshName, %oldSize, %newSize ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + + // --- DETAILS TAB --- + // Move the mesh to the new location in the tree + %selected = ShapeEdDetailTree.getSelectedItem(); + %id = ShapeEdDetailTree.findItemByName( %meshName SPC %oldSize ); + ShapeEdDetailTree.removeMeshEntry( %meshName SPC %oldSize ); + %newId = ShapeEdDetailTree.addMeshEntry( %meshName SPC %newSize ); + + // Re-select the new entry if it was selected + if ( %selected == %id ) + { + ShapeEdDetailTree.clearSelection(); + ShapeEdDetailTree.selectItem( %newId ); + } +} + +function ShapeEdPropWindow::update_onMeshRemoved( %this, %meshName ) +{ + // --- MISC --- + ShapeEdShapeView.refreshShape(); + + // --- COLLISION WINDOW --- + // Remove object from target list if it no longer exists + %objName = stripTrailingNumber( %meshName ); + if ( ShapeEditor.shape.getObjectIndex( %objName ) == -1 ) + { + %id = ShapeEdColWindow-->colTarget.findText( %objName ); + if ( %id != -1 ) + ShapeEdColWindow-->colTarget.clearEntry( %id ); + } + + // --- DETAILS TAB --- + // Determine which item to select next + %id = ShapeEdDetailTree.findItemByName( %meshName ); + if ( %id > 0 ) + { + %nextId = ShapeEdDetailTree.getPrevSibling( %id ); + if ( %nextId <= 0 ) + { + %nextId = ShapeEdDetailTree.getNextSibling( %id ); + if ( %nextId <= 0 ) + %nextId = 2; + } + + // Remove the entry from the tree + %meshSize = getTrailingNumber( %meshName ); + ShapeEdDetailTree.removeMeshEntry( %meshName, %meshSize ); + + // Change selection if needed + if ( ShapeEdDetailTree.getSelectedItem() == -1 ) + ShapeEdDetailTree.selectItem( %nextId ); + } +} + +function ShapeEdDetailTree::onSelect( %this, %id ) +{ + %name = %this.getItemText( %id ); + %baseName = stripTrailingNumber( %name ); + %size = getTrailingNumber( %name ); + + ShapeEdDetails-->meshName.setText( %baseName ); + ShapeEdDetails-->meshSize.setText( %size ); + + // Select the appropriate detail level + %dl = %this.getDetailLevelFromItem( %id ); + ShapeEdShapeView.currentDL = %dl; + + if ( %this.isDetailItem( %id ) ) + { + // Selected a detail => disable mesh controls + ShapeEdDetails-->editMeshInactive.setVisible( true ); + ShapeEdShapeView.selectedObject = -1; + ShapeEdShapeView.selectedObjDetail = 0; + } + else + { + // Selected a mesh => sync mesh controls + ShapeEdDetails-->editMeshInactive.setVisible( false ); + + switch$ ( ShapeEditor.shape.getMeshType( %name ) ) + { + case "normal": ShapeEdDetails-->bbType.setSelected( 0, false ); + case "billboard": ShapeEdDetails-->bbType.setSelected( 1, false ); + case "billboardzaxis": ShapeEdDetails-->bbType.setSelected( 2, false ); + } + + %node = ShapeEditor.shape.getObjectNode( %baseName ); + if ( %node $= "" ) + %node = "<root>"; + ShapeEdDetails-->objectNode.setSelected( ShapeEdDetails-->objectNode.findText( %node ), false ); + ShapeEdShapeView.selectedObject = ShapeEditor.shape.getObjectIndex( %baseName ); + ShapeEdShapeView.selectedObjDetail = %dl; + } +} + +function ShapeEdDetailTree::onRightMouseUp( %this, %itemId, %mouse ) +{ + // Open context menu if this is a Mesh item + if ( !%this.isDetailItem( %itemId ) ) + { + if( !isObject( "ShapeEdMeshPopup" ) ) + { + new PopupMenu( ShapeEdMeshPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Hidden" TAB "" TAB "ShapeEdDetailTree.onHideMeshItem( %this._objName, !%this._itemHidden );"; + item[ 1 ] = "-"; + item[ 2 ] = "Hide all" TAB "" TAB "ShapeEdDetailTree.onHideMeshItem( \"\", true );"; + item[ 3 ] = "Show all" TAB "" TAB "ShapeEdDetailTree.onHideMeshItem( \"\", false );"; + }; + } + + ShapeEdMeshPopup._objName = stripTrailingNumber( %this.getItemText( %itemId ) ); + ShapeEdMeshPopup._itemHidden = ShapeEdShapeView.getMeshHidden( ShapeEdMeshPopup._objName ); + + ShapeEdMeshPopup.checkItem( 0, ShapeEdMeshPopup._itemHidden ); + ShapeEdMeshPopup.showPopup( Canvas ); + } +} + +function ShapeEdDetailTree::onHideMeshItem( %this, %objName, %hide ) +{ + if ( %hide ) + %imageId = %this._imageHidden; + else + %imageId = %this._imageNone; + + if ( %objName $= "" ) + { + // Show/hide all + ShapeEdShapeView.setAllMeshesHidden( %hide ); + for ( %parent = %this.getChild(%this.getFirstRootItem()); %parent > 0; %parent = %this.getNextSibling(%parent) ) + for ( %child = %this.getChild(%parent); %child > 0; %child = %this.getNextSibling(%child) ) + %this.setItemImages( %child, %imageId, %imageId ); + } + else + { + // Show/hide all meshes for this object + ShapeEdShapeView.setMeshHidden( %objName, %hide ); + %count = ShapeEditor.shape.getMeshCount( %objName ); + for ( %i = 0; %i < %count; %i++ ) + { + %meshName = ShapeEditor.shape.getMeshName( %objName, %i ); + %id = ShapeEdDetailTree.findItemByName( %meshName ); + if ( %id > 0 ) + %this.setItemImages( %id, %imageId, %imageId ); + } + } +} + +function ShapeEdShapeView::onDetailChanged( %this ) +{ + // Update slider + if ( mRound( ShapeEdAdvancedWindow-->detailSlider.getValue() ) != %this.currentDL ) + ShapeEdAdvancedWindow-->detailSlider.setValue( %this.currentDL ); + ShapeEdAdvancedWindow-->detailSize.setText( %this.detailSize ); + + ShapeEdDetails.update_onDetailsChanged(); + + %id = ShapeEdDetailTree.getSelectedItem(); + if ( ( %id <= 0 ) || ( %this.currentDL != ShapeEdDetailTree.getDetailLevelFromItem( %id ) ) ) + { + %id = ShapeEdDetailTree.findItemByValue( %this.detailSize ); + if ( %id > 0 ) + { + ShapeEdDetailTree.clearSelection(); + ShapeEdDetailTree.selectItem( %id ); + } + } +} + +function ShapeEdAdvancedWindow::onEditDetailSize( %this ) +{ + // Change the size of the current detail level + %oldSize = ShapeEditor.shape.getDetailLevelSize( ShapeEdShapeView.currentDL ); + %detailSize = %this-->detailSize.getText(); + ShapeEditor.doEditDetailSize( %oldSize, %detailSize ); +} + +function ShapeEdDetails::onEditName( %this ) +{ + %newName = %this-->meshName.getText(); + + // Check if we are renaming a detail or a mesh + %id = ShapeEdDetailTree.getSelectedItem(); + %oldName = ShapeEdDetailTree.getItemText( %id ); + + if ( ShapeEdDetailTree.isDetailItem( %id ) ) + { + // Rename the selected detail level + %oldSize = getTrailingNumber( %oldName ); + ShapeEditor.doRenameDetail( %oldName, %newName @ %oldSize ); + } + else + { + // Rename the selected mesh + ShapeEditor.doRenameObject( stripTrailingNumber( %oldName ), %newName ); + } +} + +function ShapeEdDetails::onEditSize( %this ) +{ + %newSize = %this-->meshSize.getText(); + + // Check if we are changing the size for a detail or a mesh + %id = ShapeEdDetailTree.getSelectedItem(); + if ( ShapeEdDetailTree.isDetailItem( %id ) ) + { + // Change the size of the selected detail level + %oldSize = ShapeEdDetailTree.getItemValue( %id ); + ShapeEditor.doEditDetailSize( %oldSize, %newSize ); + } + else + { + // Change the size of the selected mesh + %meshName = ShapeEdDetailTree.getItemText( %id ); + ShapeEditor.doEditMeshSize( %meshName, %newSize ); + } +} + +function ShapeEdDetails::onEditBBType( %this ) +{ + // This command is only valid for meshes (not details) + %id = ShapeEdDetailTree.getSelectedItem(); + if ( !ShapeEdDetailTree.isDetailItem( %id ) ) + { + %meshName = ShapeEdDetailTree.getItemText( %id ); + %bbType = ShapeEdDetails-->bbType.getText(); + switch$ ( %bbType ) + { + case "None": %bbType = "normal"; + case "Billboard": %bbType = "billboard"; + case "Z Billboard": %bbType = "billboardzaxis"; + } + ShapeEditor.doEditMeshBillboard( %meshName, %bbType ); + } +} + +function ShapeEdDetails::onSetObjectNode( %this ) +{ + // This command is only valid for meshes (not details) + %id = ShapeEdDetailTree.getSelectedItem(); + if ( !ShapeEdDetailTree.isDetailItem( %id ) ) + { + %meshName = ShapeEdDetailTree.getItemText( %id ); + %objName = stripTrailingNumber( %meshName ); + %node = %this-->objectNode.getText(); + if ( %node $= "<root>" ) + %node = ""; + ShapeEditor.doSetObjectNode( %objName, %node ); + } +} + +function ShapeEdDetails::onAddMeshFromFile( %this, %path ) +{ + if ( %path $= "" ) + { + getLoadFormatFilename( %this @ ".onAddMeshFromFile", %this.lastPath ); + return; + } + + %path = makeRelativePath( %path, getMainDotCSDir() ); + %this.lastPath = %path; + + // Determine the detail level to use for the new geometry + if ( %this-->addGeomTo.getText() $= "current detail" ) + { + %size = ShapeEditor.shape.getDetailLevelSize( ShapeEdShapeView.currentDL ); + } + else + { + // Check if the file has an LODXXX hint at the end of it + %base = fileBase( %path ); + %pos = strstr( %base, "_LOD" ); + if ( %pos > 0 ) + %size = getSubStr( %base, %pos + 4, strlen( %base ) ) + 0; + else + %size = 2; + + // Make sure size is not in use + while ( ShapeEditor.shape.getDetailLevelIndex( %size ) != -1 ) + %size++; + } + + ShapeEditor.doAddMeshFromFile( %path, %size ); +} + +function ShapeEdDetails::onDeleteMesh( %this ) +{ + %id = ShapeEdDetailTree.getSelectedItem(); + if ( ShapeEdDetailTree.isDetailItem( %id ) ) + { + %detSize = ShapeEdDetailTree.getItemValue( %id ); + ShapeEditor.doRemoveShapeData( "Detail", %detSize ); + } + else + { + %name = ShapeEdDetailTree.getItemText( %id ); + ShapeEditor.doRemoveShapeData( "Mesh", %name ); + } +} + +function ShapeEdDetails::onToggleImposter( %this, %useImposter ) +{ + %hasImposterDetail = ( ShapeEditor.shape.getImposterDetailLevel() != -1 ); + if ( %useImposter == %hasImposterDetail ) + return; + + if ( %useImposter ) + { + // Determine an unused detail size + for ( %detailSize = 0; %detailSize < 50; %detailSize++ ) + { + if ( ShapeEditor.shape.getDetailLevelIndex( %detailSize ) == -1 ) + break; + } + + // Set some initial values for the imposter + %bbEquatorSteps = 6; + %bbPolarSteps = 0; + %bbDetailLevel = 0; + %bbDimension = 128; + %bbIncludePoles = 0; + %bbPolarAngle = 0; + + // Add a new imposter detail level to the shape + ShapeEditor.doEditImposter( -1, %detailSize, %bbEquatorSteps, %bbPolarSteps, + %bbDetailLevel, %bbDimension, %bbIncludePoles, %bbPolarAngle ); + } + else + { + // Remove the imposter detail level + ShapeEditor.doRemoveImposter(); + } +} + +function ShapeEdDetails::onEditImposter( %this ) +{ + // Modify the parameters of the current imposter detail level + %detailSize = ShapeEditor.shape.getDetailLevelSize( ShapeEdShapeView.currentDL ); + %bbDimension = ShapeEdAdvancedWindow-->bbDimension.getText(); + %bbDetailLevel = ShapeEdAdvancedWindow-->bbDetailLevel.getText(); + %bbEquatorSteps = ShapeEdAdvancedWindow-->bbEquatorSteps.getText(); + %bbIncludePoles = ShapeEdAdvancedWindow-->bbIncludePoles.getValue(); + %bbPolarSteps = ShapeEdAdvancedWindow-->bbPolarSteps.getText(); + %bbPolarAngle = ShapeEdAdvancedWindow-->bbPolarAngle.getText(); + + ShapeEditor.doEditImposter( ShapeEdShapeView.currentDL, %detailSize, + %bbEquatorSteps, %bbPolarSteps, %bbDetailLevel, %bbDimension, + %bbIncludePoles, %bbPolarAngle ); +} + + +function ShapeEditor::autoAddDetails( %this, %dest ) +{ + // Sets of LOD files are named like: + // + // MyShape_LOD200.dae + // MyShape_LOD64.dae + // MyShape_LOD2.dae + // + // Determine the base name of the input file (MyShape_LOD in the example above) + // and use that to find any other shapes in the set. + %base = fileBase( %dest.baseShape ); + %pos = strstr( %base, "_LOD" ); + if ( %pos < 0 ) + { + echo( "Not an LOD shape file" ); + return; + } + + %base = getSubStr( %base, 0, %pos + 4 ); + + echo( "Base is: " @ %base ); + + %filePatterns = filePath( %dest.baseShape ) @ "/" @ %base @ "*" @ fileExt( %dest.baseShape ); + + echo( "Pattern is: " @ %filePatterns ); + + %fullPath = findFirstFileMultiExpr( %filePatterns ); + while ( %fullPath !$= "" ) + { + %fullPath = makeRelativePath( %fullPath, getMainDotCSDir() ); + + if ( %fullPath !$= %dest.baseShape ) + { + echo( "Found LOD shape file: " @ %fullPath ); + + // Determine the detail size ( number after the base name ), then add the + // new mesh + %size = strreplace( fileBase( %fullPath ), %base, "" ); + ShapeEditor.addLODFromFile( %dest, %fullPath, %size, 0 ); + } + + %fullPath = findNextFileMultiExpr( %filePatterns ); + } + + if ( %this.shape == %dest ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdDetails.update_onDetailsChanged(); + } +} + +function ShapeEditor::addLODFromFile( %this, %dest, %filename, %size, %allowUnmatched ) +{ + // Get (or create) a TSShapeConstructor object for the source shape. Need to + // exec the script manually as the resource may not have been loaded yet + %csPath = filePath( %filename ) @ "/" @ fileBase( %filename ) @ ".cs"; + if ( isFile( %csPath ) ) + exec( %csPath ); + + %source = ShapeEditor.findConstructor( %filename ); + if ( %source == -1 ) + %source = ShapeEditor.createConstructor( %filename ); + %source.lodType = "SingleSize"; + %source.singleDetailSize = %size; + + // Create a temporary TSStatic to ensure the resource is loaded + %temp = new TSStatic() { + shapeName = %filename; + collisionType = "None"; + }; + + %meshList = ""; + if ( isObject( %temp ) ) + { + // Add a new mesh for each object in the source shape + %objCount = %source.getObjectCount(); + for ( %i = 0; %i < %objCount; %i++ ) + { + %objName = %source.getObjectName( %i ); + + echo( "Checking for object " @ %objName ); + + if ( %allowUnmatched || ( %dest.getObjectIndex( %objName ) != -1 ) ) + { + // Add the source object's highest LOD mesh to the destination shape + echo( "Adding detail size" SPC %size SPC "for object" SPC %objName ); + %srcName = %source.getMeshName( %objName, 0 ); + %destName = %objName SPC %size; + %dest.addMesh( %destName, %filename, %srcName ); + %meshList = %meshList TAB %destName; + } + } + + %temp.delete(); + } + + return trim( %meshList ); +} + +//------------------------------------------------------------------------------ +// Collision editing +//------------------------------------------------------------------------------ + +function ShapeEdColWindow::onWake( %this ) +{ + %this-->colType.clear(); + %this-->colType.add( "Box" ); + %this-->colType.add( "Sphere" ); + %this-->colType.add( "Capsule" ); + %this-->colType.add( "10-DOP X" ); + %this-->colType.add( "10-DOP Y" ); + %this-->colType.add( "10-DOP Z" ); + %this-->colType.add( "18-DOP" ); + %this-->colType.add( "26-DOP" ); + %this-->colType.add( "Convex Hulls" ); +} + +function ShapeEdColWindow::update_onShapeSelectionChanged( %this ) +{ + %this.lastColSettings = "" TAB "Bounds"; + + // Initialise collision mesh target list + %this-->colTarget.clear(); + %this-->colTarget.add( "Bounds" ); + %objCount = ShapeEditor.shape.getObjectCount(); + for ( %i = 0; %i < %objCount; %i++ ) + %this-->colTarget.add( ShapeEditor.shape.getObjectName( %i ) ); + + %this-->colTarget.setSelected( %this-->colTarget.findText( "Bounds" ), false ); +} + +function ShapeEdColWindow::update_onCollisionChanged( %this ) +{ + // Sync collision settings + %colData = %this.lastColSettings; + + %typeId = %this-->colType.findText( getField( %colData, 0 ) ); + %this-->colType.setSelected( %typeId, false ); + + %targetId = %this-->colTarget.findText( getField( %colData, 1 ) ); + %this-->colTarget.setSelected( %targetId, false ); + + if ( %this-->colType.getText() $= "Convex Hulls" ) + { + %this-->hullInactive.setVisible( false ); + %this-->hullDepth.setValue( getField( %colData, 2 ) ); + %this-->hullDepthText.setText( mFloor( %this-->hullDepth.getValue() ) ); + %this-->hullMergeThreshold.setValue( getField( %colData, 3 ) ); + %this-->hullMergeText.setText( mFloor( %this-->hullMergeThreshold.getValue() ) ); + %this-->hullConcaveThreshold.setValue( getField( %colData, 4 ) ); + %this-->hullConcaveText.setText( mFloor( %this-->hullConcaveThreshold.getValue() ) ); + %this-->hullMaxVerts.setValue( getField( %colData, 5 ) ); + %this-->hullMaxVertsText.setText( mFloor( %this-->hullMaxVerts.getValue() ) ); + %this-->hullMaxBoxError.setValue( getField( %colData, 6 ) ); + %this-->hullMaxBoxErrorText.setText( mFloor( %this-->hullMaxBoxError.getValue() ) ); + %this-->hullMaxSphereError.setValue( getField( %colData, 7 ) ); + %this-->hullMaxSphereErrorText.setText( mFloor( %this-->hullMaxSphereError.getValue() ) ); + %this-->hullMaxCapsuleError.setValue( getField( %colData, 8 ) ); + %this-->hullMaxCapsuleErrorText.setText( mFloor( %this-->hullMaxCapsuleError.getValue() ) ); + } + else + { + %this-->hullInactive.setVisible( true ); + } +} + +function ShapeEdColWindow::editCollision( %this ) +{ + // If the shape already contains a collision detail size-1, warn the user + // that it will be removed + if ( ( ShapeEditor.shape.getDetailLevelIndex( -1 ) >= 0 ) && + ( getField(%this.lastColSettings, 0) $= "" ) ) + { + MessageBoxYesNo( "Warning", "Existing collision geometry at detail size " @ + "-1 will be removed, and this cannot be undone. Do you want to continue?", + "ShapeEdColWindow.editCollisionOK();", "" ); + } + else + { + %this.editCollisionOK(); + } +} + +function ShapeEdColWindow::editCollisionOK( %this ) +{ + %type = %this-->colType.getText(); + %target = %this-->colTarget.getText(); + %depth = %this-->hullDepth.getValue(); + %merge = %this-->hullMergeThreshold.getValue(); + %concavity = %this-->hullConcaveThreshold.getValue(); + %maxVerts = %this-->hullMaxVerts.getValue(); + %maxBox = %this-->hullMaxBoxError.getValue(); + %maxSphere = %this-->hullMaxSphereError.getValue(); + %maxCapsule = %this-->hullMaxCapsuleError.getValue(); + + ShapeEditor.doEditCollision( %type, %target, %depth, %merge, %concavity, %maxVerts, + %maxBox, %maxSphere, %maxCapsule ); +} + +//------------------------------------------------------------------------------ +// Mounted Shapes +//------------------------------------------------------------------------------ + +function ShapeEdMountWindow::onWake( %this ) +{ + %this-->mountType.clear(); + %this-->mountType.add( "Object", 0 ); + %this-->mountType.add( "Image", 1 ); + %this-->mountType.add( "Wheel", 2 ); + %this-->mountType.setSelected( 1, false ); + + %this-->mountSeq.clear(); + %this-->mountSeq.add( "<rootpose>", 0 ); + %this-->mountSeq.setSelected( 0, false ); + %this-->mountPlayBtn.setStateOn( false ); + + // Only add the Browse entry the first time so we keep any files the user has + // set up previously + if ( ShapeEdMountShapeMenu.size() == 0 ) + { + ShapeEdMountShapeMenu.add( "Browse...", 0 ); + ShapeEdMountShapeMenu.setSelected( 0, false ); + } +} + +function ShapeEdMountWindow::isMountableNode( %this, %nodeName ) +{ + return ( startswith( %nodeName, "mount" ) || startswith( %nodeName, "hub" ) ); +} + +function ShapeEdMountWindow::update_onShapeSelectionChanged( %this ) +{ + %this.unmountAll(); + + // Initialise the dropdown menus + %this-->mountNode.clear(); + %this-->mountNode.add( "<origin>" ); + %count = ShapeEditor.shape.getNodeCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %name = ShapeEditor.shape.getNodeName( %i ); + if ( %this.isMountableNode( %name ) ) + %this-->mountNode.add( %name ); + } + %this-->mountNode.sort(); + %this-->mountNode.setFirstSelected(); + + %this-->mountSeq.clear(); + %this-->mountSeq.add( "<rootpose>", 0 ); + %this-->mountSeq.setSelected( 0, false ); +} + +function ShapeEdMountWindow::update_onMountSelectionChanged( %this ) +{ + %row = %this-->mountList.getSelectedRow(); + if ( %row > 0 ) + { + %text = %this-->mountList.getRowText( %row ); + %shapePath = getField( %text, 0 ); + + ShapeEdMountShapeMenu.setText( %shapePath ); + %this-->mountNode.setText( getField( %text, 2 ) ); + %this-->mountType.setText( getField( %text, 3 ) ); + + // Fill in sequence list + %this-->mountSeq.clear(); + %this-->mountSeq.add( "<rootpose>", 0 ); + + %tss = ShapeEditor.findConstructor( %shapePath ); + if ( !isObject( %tss ) ) + %tss = ShapeEditor.createConstructor( %shapePath ); + if ( isObject( %tss ) ) + { + %count = %tss.getSequenceCount(); + for ( %i = 0; %i < %count; %i++ ) + %this-->mountSeq.add( %tss.getSequenceName( %i ) ); + } + + // Select the currently playing sequence + %slot = %row - 1; + %seq = ShapeEdShapeView.getMountThreadSequence( %slot ); + %id = %this-->mountSeq.findText( %seq ); + if ( %id == -1 ) + %id = 0; + %this-->mountSeq.setSelected( %id, false ); + + ShapeEdMountSeqSlider.setValue( ShapeEdShapeView.getMountThreadPos( %slot ) ); + %this-->mountPlayBtn.setStateOn( ShapeEdShapeView.getMountThreadPos( %slot ) != 0 ); + } +} + +function ShapeEdMountWindow::updateSelectedMount( %this ) +{ + %row = %this-->mountList.getSelectedRow(); + if ( %row > 0 ) + %this.mountShape( %row-1 ); +} + +function ShapeEdMountWindow::setMountThreadSequence( %this ) +{ + %row = %this-->mountList.getSelectedRow(); + if ( %row > 0 ) + { + ShapeEdShapeView.setMountThreadSequence( %row-1, %this-->mountSeq.getText() ); + ShapeEdShapeView.setMountThreadDir( %row-1, %this-->mountPlayBtn.getValue() ); + } +} + +function ShapeEdMountSeqSlider::onMouseDragged( %this ) +{ + %row = ShapeEdMountWindow-->mountList.getSelectedRow(); + if ( %row > 0 ) + { + ShapeEdShapeView.setMountThreadPos( %row-1, %this.getValue() ); + + // Pause the sequence when the slider is dragged + ShapeEdShapeView.setMountThreadDir( %row-1, 0 ); + ShapeEdMountWindow-->mountPlayBtn.setStateOn( false ); + } +} + +function ShapeEdMountWindow::toggleMountThreadPlayback( %this ) +{ + %row = %this-->mountList.getSelectedRow(); + if ( %row > 0 ) + ShapeEdShapeView.setMountThreadDir( %row-1, %this-->mountPlayBtn.getValue() ); +} + +function ShapeEdMountShapeMenu::onSelect( %this, %id, %text ) +{ + if ( %text $= "Browse..." ) + { + // Allow the user to browse for an external model file + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); + } + else + { + // Modify the current mount + ShapeEdMountWindow.updateSelectedMount(); + } +} + +function ShapeEdMountShapeMenu::onBrowseSelect( %this, %path ) +{ + %path = makeRelativePath( %path, getMainDotCSDir() ); + %this.lastPath = %path; + %this.setText( %path ); + + // Add entry if unique + if ( %this.findText( %path ) == -1 ) + %this.add( %path ); + + ShapeEdMountWindow.updateSelectedMount(); +} + +function ShapeEdMountWindow::mountShape( %this, %slot ) +{ + %model = ShapeEdMountShapeMenu.getText(); + %node = %this-->mountNode.getText(); + %type = %this-->mountType.getText(); + + if ( %model $= "Browse..." ) + %model = "core/art/shapes/octahedron.dts"; + + if ( ShapeEdShapeView.mountShape( %model, %node, %type, %slot ) ) + { + %rowText = %model TAB fileName( %model ) TAB %node TAB %type; + if ( %slot == -1 ) + { + %id = %this.mounts++; + %this-->mountList.addRow( %id, %rowText ); + } + else + { + %id = %this-->mountList.getRowId( %slot+1 ); + %this-->mountList.setRowById( %id, %rowText ); + } + + %this-->mountList.setSelectedById( %id ); + } + else + { + MessageBoxOK( "Error", "Failed to mount \"" @ %model @ "\". Check the console for error messages.", "" ); + } +} + +function ShapeEdMountWindow::unmountShape( %this ) +{ + %row = %this-->mountList.getSelectedRow(); + if ( %row > 0 ) + { + ShapeEdShapeView.unmountShape( %row-1 ); + %this-->mountList.removeRow( %row ); + + // Select the next row (if any) + %count = %this-->mountList.rowCount(); + if ( %row >= %count ) + %row = %count-1; + if ( %row > 0 ) + %this-->mountList.setSelectedRow( %row ); + } +} + +function ShapeEdMountWindow::unmountAll( %this ) +{ + ShapeEdShapeView.unmountAll(); + %this-->mountList.clear(); + %this-->mountList.addRow( -1, "FullPath" TAB "Filename" TAB "Node" TAB "Type" ); + %this-->mountList.setRowActive( -1, false ); +} + +//------------------------------------------------------------------------------ +// Shape Preview +//------------------------------------------------------------------------------ + +function ShapeEdPreviewGui::updatePreviewBackground( %color ) +{ + ShapeEdPreviewGui-->previewBackground.color = %color; + ShapeEditorToolbar-->previewBackgroundPicker.color = %color; +} + +function showShapeEditorPreview() +{ + %visible = ShapeEditorToolbar-->showPreview.getValue(); + ShapeEdPreviewGui.setVisible( %visible ); +} diff --git a/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs new file mode 100644 index 000000000..2c48b25a1 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorActions.ed.cs @@ -0,0 +1,1304 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// The TSShapeConstructor object allows you to apply a set of transformations +// to a 3space shape after it is loaded by Torque, but _before_ the shape is used +// by any other object (eg. Player, StaticShape etc). The sort of transformations +// available include adding, renaming and removing nodes and sequences. This GUI +// is a visual wrapper around TSShapeConstructor which allows you to build up the +// transformation set without having to get your hands dirty with TorqueScript. +// +// Removing a node, sequence, mesh or detail poses a problem. These operations +// permanently delete a potentially large amount of data scattered throughout +// the shape, and there is no easy way to restore it if the user 'undoes' the +// delete. Although it is possible to store the deleted data somewhere and restore +// it on undo, it is not easy to get right, and ugly as hell to implement. For +// example, removing a node would require storing the node name, the +// translation/rotation/scale matters bit for each sequence, all node transform +// keyframes, the IDs of any objects that were attached to the node, skin weights +// etc, then restoring all that data into the original place on undo. Frankly, +// TSShape was never designed to be modified dynamically like that. +// +// So......currently we wimp out completely and just don't support undo for those +// remove operations. Lame, I know, but the best I can do for now. +// +// This file implements all of the actions that can be applied by the GUI. Each +// action has 3 methods: +// +// doit: called the first time the action is performed +// undo: called to undo the action +// redo: called to redo the action (usually the same as doit) +// +// In each case, the appropriate change is made to the shape, and the GUI updated. +// +// TSShapeConstructor keeps track of all the changes made and provides a simple +// way to save the modifications back out to a script file. + +// The ShapeEditor uses its own UndoManager +if ( !isObject( ShapeEdUndoManager ) ) + new UndoManager( ShapeEdUndoManager ); + +function ShapeEdUndoManager::updateUndoMenu( %this, %editMenu ) +{ + %undoName = %this.getNextUndoName(); + %redoName = %this.getNextRedoName(); + + %editMenu.setItemName( 0, "Undo " @ %undoName ); + %editMenu.setItemName( 1, "Redo " @ %redoName ); + + %editMenu.enableItem( 0, %undoName !$= "" ); + %editMenu.enableItem( 1, %redoName !$= "" ); +} + +//------------------------------------------------------------------------------ +// Helper functions for creating and applying GUI operations + +function ShapeEditor::createAction( %this, %class, %desc ) +{ + pushInstantGroup(); + %action = new UndoScriptAction() + { + class = %class; + superClass = BaseShapeEdAction; + actionName = %desc; + done = 0; + }; + popInstantGroup(); + return %action; +} + +function ShapeEditor::doAction( %this, %action ) +{ + if ( %action.doit() ) + { + ShapeEditor.setDirty( true ); + %action.addToManager( ShapeEdUndoManager ); + } + else + { + MessageBoxOK( "Error", %action.actionName SPC "failed. Check the console for error messages.", "" ); + } +} + +function BaseShapeEdAction::redo( %this ) +{ + // Default redo action is the same as the doit action + if ( %this.doit() ) + { + ShapeEditor.setDirty( true ); + } + else + { + MessageBoxOK( "Error", "Redo" SPC %this.actionName SPC "failed. Check the console for error messages.", "" ); + } +} + +function BaseShapeEdAction::undo( %this ) +{ + ShapeEditor.setDirty( true ); +} + +//------------------------------------------------------------------------------ + +function ShapeEditor::doRemoveShapeData( %this, %type, %name ) +{ + // Removing data from the shape cannot be undone => so warn the user first + MessageBoxYesNo( "Warning", "Deleting a " @ %type @ " cannot be undone. Do " @ + "you want to continue?", "ShapeEditor.doRemove" @ %type @ "( \"" @ %name @ "\" );", "" ); +} + +//------------------------------------------------------------------------------ +// Add node +function ShapeEditor::doAddNode( %this, %nodeName, %parentName, %transform ) +{ + %action = %this.createAction( ActionAddNode, "Add node" ); + %action.nodeName = %nodeName; + %action.parentName = %parentName; + %action.transform = %transform; + + %this.doAction( %action ); +} + +function ActionAddNode::doit( %this ) +{ + if ( ShapeEditor.shape.addNode( %this.nodeName, %this.parentName, %this.transform ) ) + { + ShapeEdPropWindow.update_onNodeAdded( %this.nodeName, -1 ); + return true; + } + return false; +} + +function ActionAddNode::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.removeNode( %this.nodeName ) ) + ShapeEdPropWindow.update_onNodeRemoved( %this.nodeName, 1 ); +} + +//------------------------------------------------------------------------------ +// Remove node +function ShapeEditor::doRemoveNode( %this, %nodeName ) +{ + %action = %this.createAction( ActionRemoveNode, "Remove node" ); + %action.nodeName =%nodeName; + %action.nodeChildIndex = ShapeEdNodeTreeView.getChildIndexByName( %nodeName ); + + // Need to delete all child nodes of this node as well, so recursively collect + // all of the names. + %action.nameList = %this.getNodeNames( %nodeName, "" ); + %action.nameCount = getFieldCount( %action.nameList ); + for ( %i = 0; %i < %action.nameCount; %i++ ) + %action.names[%i] = getField( %action.nameList, %i ); + + %this.doAction( %action ); +} + +function ActionRemoveNode::doit( %this ) +{ + for ( %i = 0; %i < %this.nameCount; %i++ ) + ShapeEditor.shape.removeNode( %this.names[%i] ); + + // Update GUI + ShapeEdPropWindow.update_onNodeRemoved( %this.nameList, %this.nameCount ); + + return true; +} + +function ActionRemoveNode::undo( %this ) +{ + Parent::undo( %this ); +} + +//------------------------------------------------------------------------------ +// Rename node +function ShapeEditor::doRenameNode( %this, %oldName, %newName ) +{ + %action = %this.createAction( ActionRenameNode, "Rename node" ); + %action.oldName = %oldName; + %action.newName = %newName; + + %this.doAction( %action ); +} + +function ActionRenameNode::doit( %this ) +{ + if ( ShapeEditor.shape.renameNode( %this.oldName, %this.newName ) ) + { + ShapeEdPropWindow.update_onNodeRenamed( %this.oldName, %this.newName ); + return true; + } + return false; +} + +function ActionRenameNode::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.renameNode( %this.newName, %this.oldName ) ) + ShapeEdPropWindow.update_onNodeRenamed( %this.newName, %this.oldName ); +} + +//------------------------------------------------------------------------------ +// Set node parent +function ShapeEditor::doSetNodeParent( %this, %name, %parent ) +{ + if ( %parent $= "<root>" ) + %parent = ""; + + %action = %this.createAction( ActionSetNodeParent, "Set parent node" ); + %action.nodeName = %name; + %action.parentName = %parent; + %action.oldParentName = ShapeEditor.shape.getNodeParentName( %name ); + + %this.doAction( %action ); +} + +function ActionSetNodeParent::doit( %this ) +{ + if ( ShapeEditor.shape.setNodeParent( %this.nodeName, %this.parentName ) ) + { + ShapeEdPropWindow.update_onNodeParentChanged( %this.nodeName ); + return true; + } + return false; +} + +function ActionSetNodeParent::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.setNodeParent( %this.nodeName, %this.oldParentName ) ) + ShapeEdPropWindow.update_onNodeParentChanged( %this.nodeName ); +} + +//------------------------------------------------------------------------------ +// Edit node transform +function ShapeEditor::doEditNodeTransform( %this, %nodeName, %newTransform, %isWorld, %gizmoID ) +{ + // If dragging the 3D gizmo, combine all movement into a single action. Undoing + // that action will return the node to where it was when the gizmo drag started. + %last = ShapeEdUndoManager.getUndoAction( ShapeEdUndoManager.getUndoCount() - 1 ); + if ( ( %last != -1 ) && ( %last.class $= ActionEditNodeTransform ) && + ( %last.nodeName $= %nodeName ) && ( %last.gizmoID != -1 ) && ( %last.gizmoID == %gizmoID ) ) + { + // Use the last action to do the edit, and modify it so it only applies + // the latest transform + %last.newTransform = %newTransform; + %last.isWorld = %isWorld; + %last.doit(); + ShapeEditor.setDirty( true ); + } + else + { + %action = %this.createAction( ActionEditNodeTransform, "Edit node transform" ); + %action.nodeName = %nodeName; + %action.newTransform = %newTransform; + %action.isWorld = %isWorld; + %action.gizmoID = %gizmoID; + %action.oldTransform = %this.shape.getNodeTransform( %nodeName, %isWorld ); + + %this.doAction( %action ); + } +} + +function ActionEditNodeTransform::doit( %this ) +{ + ShapeEditor.shape.setNodeTransform( %this.nodeName, %this.newTransform, %this.isWorld ); + ShapeEdPropWindow.update_onNodeTransformChanged(); + return true; +} + +function ActionEditNodeTransform::undo( %this ) +{ + Parent::undo( %this ); + + ShapeEditor.shape.setNodeTransform( %this.nodeName, %this.oldTransform, %this.isWorld ); + ShapeEdPropWindow.update_onNodeTransformChanged(); +} + +//------------------------------------------------------------------------------ +// Add sequence +function ShapeEditor::doAddSequence( %this, %seqName, %from, %start, %end ) +{ + %action = %this.createAction( ActionAddSequence, "Add sequence" ); + %action.seqName = %seqName; + %action.origFrom = %from; + %action.from = %from; + %action.start = %start; + %action.end = %end; + + %this.doAction( %action ); +} + +function ActionAddSequence::doit( %this ) +{ + // If adding this sequence from an existing sequence, make a backup copy of + // the existing sequence first, so we can edit the start/end frames later + // without having to worry if the original source sequence has changed. + if ( ShapeEditor.shape.getSequenceIndex( %this.from ) >= 0 ) + { + %this.from = ShapeEditor.getUniqueName( "sequence", "__backup__" @ %this.origFrom @ "_" ); + ShapeEditor.shape.addSequence( %this.origFrom, %this.from ); + } + + // Add the sequence + $collada::forceLoadDAE = EditorSettings.value( "forceLoadDAE" ); + %success = ShapeEditor.shape.addSequence( %this.from, %this.seqName, %this.start, %this.end ); + $collada::forceLoadDAE = false; + + if ( %success ) + { + ShapeEdPropWindow.update_onSequenceAdded( %this.seqName, -1 ); + return true; + } + return false; +} + +function ActionAddSequence::undo( %this ) +{ + Parent::undo( %this ); + + // Remove the backup sequence if one was created + if ( %this.origFrom !$= %this.from ) + { + ShapeEditor.shape.removeSequence( %this.from ); + %this.from = %this.origFrom; + } + + // Remove the actual sequence + if ( ShapeEditor.shape.removeSequence( %this.seqName ) ) + ShapeEdPropWindow.update_onSequenceRemoved( %this.seqName ); +} + +//------------------------------------------------------------------------------ +// Remove sequence + +function ShapeEditor::doRemoveSequence( %this, %seqName ) +{ + %action = %this.createAction( ActionRemoveSequence, "Remove sequence" ); + %action.seqName = %seqName; + + %this.doAction( %action ); +} + +function ActionRemoveSequence::doit( %this ) +{ + if ( ShapeEditor.shape.removeSequence( %this.seqName ) ) + { + ShapeEdPropWindow.update_onSequenceRemoved( %this.seqName ); + return true; + } + return false; +} + +function ActionRemoveSequence::undo( %this ) +{ + Parent::undo( %this ); +} + +//------------------------------------------------------------------------------ +// Rename sequence +function ShapeEditor::doRenameSequence( %this, %oldName, %newName ) +{ + %action = %this.createAction( ActionRenameSequence, "Rename sequence" ); + %action.oldName = %oldName; + %action.newName = %newName; + + %this.doAction( %action ); +} + +function ActionRenameSequence::doit( %this ) +{ + if ( ShapeEditor.shape.renameSequence( %this.oldName, %this.newName ) ) + { + ShapeEdPropWindow.update_onSequenceRenamed( %this.oldName, %this.newName ); + return true; + } + return false; +} + +function ActionRenameSequence::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.renameSequence( %this.newName, %this.oldName ) ) + ShapeEdPropWindow.update_onSequenceRenamed( %this.newName, %this.oldName ); +} + +//------------------------------------------------------------------------------ +// Edit sequence source data ( parent, start or end ) +function ShapeEditor::doEditSeqSource( %this, %seqName, %from, %start, %end ) +{ + %action = %this.createAction( ActionEditSeqSource, "Edit sequence source data" ); + %action.seqName = %seqName; + %action.origFrom = %from; + %action.from = %from; + %action.start = %start; + %action.end = %end; + + // To support undo, the sequence will be renamed instead of removed (undo just + // removes the added sequence and renames the original back). Generate a unique + // name for the backed up sequence + %action.seqBackup = ShapeEditor.getUniqueName( "sequence", "__backup__" @ %action.seqName @ "_" ); + + // If editing an internal sequence, the source is the renamed backup + if ( %action.from $= %action.seqName ) + %action.from = %action.seqBackup; + + %this.doAction( %action ); +} + +function ActionEditSeqSource::doit( %this ) +{ + // If changing the source to an existing sequence, make a backup copy of + // the existing sequence first, so we can edit the start/end frames later + // without having to worry if the original source sequence has changed. + if ( !startswith( %this.from, "__backup__" ) && + ShapeEditor.shape.getSequenceIndex( %this.from ) >= 0 ) + { + %this.from = ShapeEditor.getUniqueName( "sequence", "__backup__" @ %this.origFrom @ "_" ); + ShapeEditor.shape.addSequence( %this.origFrom, %this.from ); + } + + // Get settings we want to retain + %priority = ShapeEditor.shape.getSequencePriority( %this.seqName ); + %cyclic = ShapeEditor.shape.getSequenceCyclic( %this.seqName ); + %blend = ShapeEditor.shape.getSequenceBlend( %this.seqName ); + + // Rename this sequence (instead of removing it) so we can undo this action + ShapeEditor.shape.renameSequence( %this.seqName, %this.seqBackup ); + + // Add the new sequence + if ( ShapeEditor.shape.addSequence( %this.from, %this.seqName, %this.start, %this.end ) ) + { + // Restore original settings + if ( ShapeEditor.shape.getSequencePriority ( %this.seqName ) != %priority ) + ShapeEditor.shape.setSequencePriority( %this.seqName, %priority ); + if ( ShapeEditor.shape.getSequenceCyclic( %this.seqName ) != %cyclic ) + ShapeEditor.shape.setSequenceCyclic( %this.seqName, %cyclic ); + + %newBlend = ShapeEditor.shape.getSequenceBlend( %this.seqName ); + if ( %newBlend !$= %blend ) + { + // Undo current blend, then apply new one + ShapeEditor.shape.setSequenceBlend( %this.seqName, 0, getField( %newBlend, 1 ), getField( %newBlend, 2 ) ); + if ( getField( %blend, 0 ) == 1 ) + ShapeEditor.shape.setSequenceBlend( %this.seqName, getField( %blend, 0 ), getField( %blend, 1 ), getField( %blend, 2 ) ); + } + + if ( ShapeEdSequenceList.getSelectedName() $= %this.seqName ) + { + ShapeEdSequenceList.editColumn( %this.seqName, 3, %this.end - %this.start + 1 ); + ShapeEdPropWindow.syncPlaybackDetails(); + } + + return true; + } + return false; +} + +function ActionEditSeqSource::undo( %this ) +{ + Parent::undo( %this ); + + // Remove the source sequence backup if one was created + if ( ( %this.from !$= %this.origFrom ) && ( %this.from !$= %this.seqBackup ) ) + { + ShapeEditor.shape.removeSequence( %this.from ); + %this.from = %this.origFrom; + } + + // Remove the added sequence, and rename the backup back + if ( ShapeEditor.shape.removeSequence( %this.seqName ) && + ShapeEditor.shape.renameSequence( %this.seqBackup, %this.seqName ) ) + { + if ( ShapeEdSequenceList.getSelectedName() $= %this.seqName ) + { + ShapeEdSequenceList.editColumn( %this.seqName, 3, %this.end - %this.start + 1 ); + ShapeEdPropWindow.syncPlaybackDetails(); + } + } +} + +//------------------------------------------------------------------------------ +// Edit cyclic flag +function ShapeEditor::doEditCyclic( %this, %seqName, %cyclic ) +{ + %action = %this.createAction( ActionEditCyclic, "Toggle cyclic flag" ); + %action.seqName = %seqName; + %action.cyclic = %cyclic; + + %this.doAction( %action ); +} + +function ActionEditCyclic::doit( %this ) +{ + if ( ShapeEditor.shape.setSequenceCyclic( %this.seqName, %this.cyclic ) ) + { + ShapeEdPropWindow.update_onSequenceCyclicChanged( %this.seqName, %this.cyclic ); + return true; + } + return false; +} + +function ActionEditCyclic::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.setSequenceCyclic( %this.seqName, !%this.cyclic ) ) + ShapeEdPropWindow.update_onSequenceCyclicChanged( %this.seqName, !%this.cyclic ); +} + +//------------------------------------------------------------------------------ +// Edit blend properties +function ShapeEditor::doEditBlend( %this, %seqName, %blend, %blendSeq, %blendFrame ) +{ + %action = %this.createAction( ActionEditBlend, "Edit blend properties" ); + %action.seqName = %seqName; + %action.blend = %blend; + %action.blendSeq = %blendSeq; + %action.blendFrame = %blendFrame; + + // Store the current blend settings + %oldBlend = ShapeEditor.shape.getSequenceBlend( %seqName ); + %action.oldBlend = getField( %oldBlend, 0 ); + %action.oldBlendSeq = getField( %oldBlend, 1 ); + %action.oldBlendFrame = getField( %oldBlend, 2 ); + + // Use new values if the old ones do not exist ( for blend sequences embedded + // in the DTS/DSQ file ) + if ( %action.oldBlendSeq $= "" ) + %action.oldBlendSeq = %action.blendSeq; + if ( %action.oldBlendFrame $= "" ) + %action.oldBlendFrame = %action.blendFrame; + + %this.doAction( %action ); +} + +function ActionEditBlend::doit( %this ) +{ + // If we are changing the blend reference ( rather than just toggling the flag ) + // we need to undo the current blend first. + if ( %this.blend && %this.oldBlend ) + { + if ( !ShapeEditor.shape.setSequenceBlend( %this.seqName, false, %this.oldBlendSeq, %this.oldBlendFrame ) ) + return false; + } + + if ( ShapeEditor.shape.setSequenceBlend( %this.seqName, %this.blend, %this.blendSeq, %this.blendFrame ) ) + { + ShapeEdPropWindow.update_onSequenceBlendChanged( %this.seqName, %this.blend, + %this.oldBlendSeq, %this.oldBlendFrame, %this.blendSeq, %this.blendFrame ); + return true; + } + return false; +} + +function ActionEditBlend::undo( %this ) +{ + Parent::undo( %this ); + + // If we are changing the blend reference ( rather than just toggling the flag ) + // we need to undo the current blend first. + if ( %this.blend && %this.oldBlend ) + { + if ( !ShapeEditor.shape.setSequenceBlend( %this.seqName, false, %this.blendSeq, %this.blendFrame ) ) + return; + } + + if ( ShapeEditor.shape.setSequenceBlend( %this.seqName, %this.oldBlend, %this.oldBlendSeq, %this.oldBlendFrame ) ) + { + ShapeEdPropWindow.update_onSequenceBlendChanged( %this.seqName, !%this.blend, + %this.blendSeq, %this.blendFrame, %this.oldBlendSeq, %this.oldBlendFrame ); + } +} + +//------------------------------------------------------------------------------ +// Edit sequence priority +function ShapeEditor::doEditSequencePriority( %this, %seqName, %newPriority ) +{ + %action = %this.createAction( ActionEditSequencePriority, "Edit sequence priority" ); + %action.seqName = %seqName; + %action.newPriority = %newPriority; + %action.oldPriority = %this.shape.getSequencePriority( %seqName ); + + %this.doAction( %action ); +} + +function ActionEditSequencePriority::doit( %this ) +{ + if ( ShapeEditor.shape.setSequencePriority( %this.seqName, %this.newPriority ) ) + { + ShapeEdPropWindow.update_onSequencePriorityChanged( %this.seqName ); + return true; + } + return false; +} + +function ActionEditSequencePriority::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.setSequencePriority( %this.seqName, %this.oldPriority ) ) + ShapeEdPropWindow.update_onSequencePriorityChanged( %this.seqName ); +} + +//------------------------------------------------------------------------------ +// Edit sequence ground speed +function ShapeEditor::doEditSequenceGroundSpeed( %this, %seqName, %newSpeed ) +{ + %action = %this.createAction( ActionEditSequenceGroundSpeed, "Edit sequence ground speed" ); + %action.seqName = %seqName; + %action.newSpeed = %newSpeed; + %action.oldSpeed = %this.shape.getSequenceGroundSpeed( %seqName ); + + %this.doAction( %action ); +} + +function ActionEditSequenceGroundSpeed::doit( %this ) +{ + if ( ShapeEditor.shape.setSequenceGroundSpeed( %this.seqName, %this.newSpeed ) ) + { + ShapeEdPropWindow.update_onSequenceGroundSpeedChanged( %this.seqName ); + return true; + } + return false; +} + +function ActionEditSequenceGroundSpeed::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.setSequenceGroundSpeed( %this.seqName, %this.oldSpeed ) ) + ShapeEdPropWindow.update_onSequenceGroundSpeedChanged( %this.seqName ); +} + +//------------------------------------------------------------------------------ +// Add trigger +function ShapeEditor::doAddTrigger( %this, %seqName, %frame, %state ) +{ + %action = %this.createAction( ActionAddTrigger, "Add trigger" ); + %action.seqName = %seqName; + %action.frame = %frame; + %action.state = %state; + + %this.doAction( %action ); +} + +function ActionAddTrigger::doit( %this ) +{ + if ( ShapeEditor.shape.addTrigger( %this.seqName, %this.frame, %this.state ) ) + { + ShapeEdPropWindow.update_onTriggerAdded( %this.seqName, %this.frame, %this.state ); + return true; + } + return false; +} + +function ActionAddTrigger::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.removeTrigger( %this.seqName, %this.frame, %this.state ) ) + ShapeEdPropWindow.update_onTriggerRemoved( %this.seqName, %this.frame, %this.state ); +} + +//------------------------------------------------------------------------------ +// Remove trigger +function ShapeEditor::doRemoveTrigger( %this, %seqName, %frame, %state ) +{ + %action = %this.createAction( ActionRemoveTrigger, "Remove trigger" ); + %action.seqName = %seqName; + %action.frame = %frame; + %action.state = %state; + + %this.doAction( %action ); +} + +function ActionRemoveTrigger::doit( %this ) +{ + if ( ShapeEditor.shape.removeTrigger( %this.seqName, %this.frame, %this.state ) ) + { + ShapeEdPropWindow.update_onTriggerRemoved( %this.seqName, %this.frame, %this.state ); + return true; + } + return false; +} + +function ActionRemoveTrigger::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.addTrigger( %this.seqName, %this.frame, %this.state ) ) + ShapeEdPropWindow.update_onTriggerAdded( %this.seqName, %this.frame, %this.state ); +} + +//------------------------------------------------------------------------------ +// Edit trigger +function ShapeEditor::doEditTrigger( %this, %seqName, %oldFrame, %oldState, %frame, %state ) +{ + %action = %this.createAction( ActionEditTrigger, "Edit trigger" ); + %action.seqName = %seqName; + %action.oldFrame = %oldFrame; + %action.oldState = %oldState; + %action.frame = %frame; + %action.state = %state; + + %this.doAction( %action ); +} + +function ActionEditTrigger::doit( %this ) +{ + if ( ShapeEditor.shape.addTrigger( %this.seqName, %this.frame, %this.state ) && + ShapeEditor.shape.removeTrigger( %this.seqName, %this.oldFrame, %this.oldState ) ) + { + ShapeEdTriggerList.updateItem( %this.oldFrame, %this.oldState, %this.frame, %this.state ); + return true; + } + return false; +} + +function ActionEditTrigger::undo( %this ) +{ + Parent::undo( %this ); + + if ( ShapeEditor.shape.addTrigger( %this.seqName, %this.oldFrame, %this.oldState ) && + ShapeEditor.shape.removeTrigger( %this.seqName, %this.frame, %this.state ) ) + ShapeEdTriggerList.updateItem( %this.frame, %this.state, %this.oldFrame, %this.oldState ); +} + +//------------------------------------------------------------------------------ +// Rename detail +function ShapeEditor::doRenameDetail( %this, %oldName, %newName ) +{ + %action = %this.createAction( ActionRenameDetail, "Rename detail" ); + %action.oldName = %oldName; + %action.newName = %newName; + + %this.doAction( %action ); +} + +function ActionRenameDetail::doit( %this ) +{ + if ( ShapeEditor.shape.renameDetailLevel( %this.oldName, %this.newName ) ) + { + ShapeEdPropWindow.update_onDetailRenamed( %this.oldName, %this.newName ); + return true; + } + return false; +} + +function ActionRenameDetail::undo( %this ) +{ + Parent::undo( %this ); + if ( ShapeEditor.shape.renameDetailLevel( %this.newName, %this.oldName ) ) + ShapeEdPropWindow.update_onDetailRenamed( %this.newName, %this.oldName ); +} + +//------------------------------------------------------------------------------ +// Edit detail size +function ShapeEditor::doEditDetailSize( %this, %oldSize, %newSize ) +{ + %action = %this.createAction( ActionEditDetailSize, "Edit detail size" ); + %action.oldSize = %oldSize; + %action.newSize = %newSize; + + %this.doAction( %action ); +} + +function ActionEditDetailSize::doit( %this ) +{ + %dl = ShapeEditor.shape.setDetailLevelSize( %this.oldSize, %this.newSize ); + if ( %dl != -1 ) + { + ShapeEdPropWindow.update_onDetailSizeChanged( %this.oldSize, %this.newSize ); + return true; + } + return false; +} + +function ActionEditDetailSize::undo( %this ) +{ + Parent::undo( %this ); + %dl = ShapeEditor.shape.setDetailLevelSize( %this.newSize, %this.oldSize ); + if ( %dl != -1 ) + ShapeEdPropWindow.update_onDetailSizeChanged( %this.newSize, %this.oldSize ); +} + +//------------------------------------------------------------------------------ +// Rename object +function ShapeEditor::doRenameObject( %this, %oldName, %newName ) +{ + %action = %this.createAction( ActionRenameObject, "Rename object" ); + %action.oldName = %oldName; + %action.newName = %newName; + + %this.doAction( %action ); +} + +function ActionRenameObject::doit( %this ) +{ + if ( ShapeEditor.shape.renameObject( %this.oldName, %this.newName ) ) + { + ShapeEdPropWindow.update_onObjectRenamed( %this.oldName, %this.newName ); + return true; + } + return false; +} + +function ActionRenameObject::undo( %this ) +{ + Parent::undo( %this ); + if ( ShapeEditor.shape.renameObject( %this.newName, %this.oldName ) ) + ShapeEdPropWindow.update_onObjectRenamed( %this.newName, %this.oldName ); +} + +//------------------------------------------------------------------------------ +// Edit mesh size +function ShapeEditor::doEditMeshSize( %this, %meshName, %size ) +{ + %action = %this.createAction( ActionEditMeshSize, "Edit mesh size" ); + %action.meshName = stripTrailingNumber( %meshName ); + %action.oldSize = getTrailingNumber( %meshName ); + %action.newSize = %size; + + %this.doAction( %action ); +} + +function ActionEditMeshSize::doit( %this ) +{ + if ( ShapeEditor.shape.setMeshSize( %this.meshName SPC %this.oldSize, %this.newSize ) ) + { + ShapeEdPropWindow.update_onMeshSizeChanged( %this.meshName, %this.oldSize, %this.newSize ); + return true; + } + return false; +} + +function ActionEditMeshSize::undo( %this ) +{ + Parent::undo( %this ); + if ( ShapeEditor.shape.setMeshSize( %this.meshName SPC %this.newSize, %this.oldSize ) ) + ShapeEdPropWindow.update_onMeshSizeChanged( %this.meshName, %this.oldSize, %this.oldSize ); +} + +//------------------------------------------------------------------------------ +// Edit billboard type +function ShapeEditor::doEditMeshBillboard( %this, %meshName, %type ) +{ + %action = %this.createAction( ActionEditMeshBillboard, "Edit mesh billboard" ); + %action.meshName = %meshName; + %action.oldType = %this.shape.getMeshType( %meshName ); + %action.newType = %type; + + %this.doAction( %action ); +} + +function ActionEditMeshBillboard::doit( %this ) +{ + if ( ShapeEditor.shape.setMeshType( %this.meshName, %this.newType ) ) + { + switch$ ( ShapeEditor.shape.getMeshType( %this.meshName ) ) + { + case "normal": ShapeEdDetails-->bbType.setSelected( 0, false ); + case "billboard": ShapeEdDetails-->bbType.setSelected( 1, false ); + case "billboardzaxis": ShapeEdDetails-->bbType.setSelected( 2, false ); + } + return true; + } + return false; +} + +function ActionEditMeshBillboard::undo( %this ) +{ + Parent::undo( %this ); + if ( ShapeEditor.shape.setMeshType( %this.meshName, %this.oldType ) ) + { + %id = ShapeEdDetailTree.getSelectedItem(); + if ( ( %id > 1 ) && ( ShapeEdDetailTree.getItemText( %id ) $= %this.meshName ) ) + { + switch$ ( ShapeEditor.shape.getMeshType( %this.meshName ) ) + { + case "normal": ShapeEdDetails-->bbType.setSelected( 0, false ); + case "billboard": ShapeEdDetails-->bbType.setSelected( 1, false ); + case "billboardzaxis": ShapeEdDetails-->bbType.setSelected( 2, false ); + } + } + } +} + +//------------------------------------------------------------------------------ +// Edit object node +function ShapeEditor::doSetObjectNode( %this, %objName, %node ) +{ + %action = %this.createAction( ActionSetObjectNode, "Set object node" ); + %action.objName = %objName; + %action.oldNode = %this.shape.getObjectNode( %objName ); + %action.newNode = %node; + + %this.doAction( %action ); +} + +function ActionSetObjectNode::doit( %this ) +{ + if ( ShapeEditor.shape.setObjectNode( %this.objName, %this.newNode ) ) + { + ShapeEdPropWindow.update_onObjectNodeChanged( %this.objName ); + return true; + } + return false; +} + +function ActionSetObjectNode::undo( %this ) +{ + Parent::undo( %this ); + if ( ShapeEditor.shape.setObjectNode( %this.objName, %this.oldNode ) ) + ShapeEdPropWindow.update_onObjectNodeChanged( %this.objName ); +} + +//------------------------------------------------------------------------------ +// Remove mesh +function ShapeEditor::doRemoveMesh( %this, %meshName ) +{ + %action = %this.createAction( ActionRemoveMesh, "Remove mesh" ); + %action.meshName = %meshName; + + %this.doAction( %action ); +} + +function ActionRemoveMesh::doit( %this ) +{ + if ( ShapeEditor.shape.removeMesh( %this.meshName ) ) + { + ShapeEdPropWindow.update_onMeshRemoved( %this.meshName ); + return true; + } + return false; +} + +function ActionRemoveMesh::undo( %this ) +{ + Parent::undo( %this ); +} + +//------------------------------------------------------------------------------ +// Add meshes from file +function ShapeEditor::doAddMeshFromFile( %this, %filename, %size ) +{ + %action = %this.createAction( ActionAddMeshFromFile, "Add mesh from file" ); + %action.filename = %filename; + %action.size = %size; + + %this.doAction( %action ); +} + +function ActionAddMeshFromFile::doit( %this ) +{ + %this.meshList = ShapeEditor.addLODFromFile( ShapeEditor.shape, %this.filename, %this.size, 1 ); + if ( %this.meshList !$= "" ) + { + %count = getFieldCount( %this.meshList ); + for ( %i = 0; %i < %count; %i++ ) + ShapeEdPropWindow.update_onMeshAdded( getField( %this.meshList, %i ) ); + + ShapeEdMaterials.updateMaterialList(); + + return true; + } + return false; +} + +function ActionAddMeshFromFile::undo( %this ) +{ + // Remove all the meshes we added + %count = getFieldCount( %this.meshList ); + for ( %i = 0; %i < %count; %i ++ ) + { + %name = getField( %this.meshList, %i ); + ShapeEditor.shape.removeMesh( %name ); + ShapeEdPropWindow.update_onMeshRemoved( %name ); + } + ShapeEdMaterials.updateMaterialList(); +} + +//------------------------------------------------------------------------------ +// Add/edit collision geometry +function ShapeEditor::doEditCollision( %this, %type, %target, %depth, %merge, %concavity, + %maxVerts, %boxMax, %sphereMax, %capsuleMax ) +{ + %colData = ShapeEdColWindow.lastColSettings; + + %action = %this.createAction( ActionEditCollision, "Edit shape collision" ); + + %action.oldType = getField( %colData, 0 ); + %action.oldTarget = getField( %colData, 1 ); + %action.oldDepth = getField( %colData, 2 ); + %action.oldMerge = getField( %colData, 3 ); + %action.oldConcavity = getField( %colData, 4 ); + %action.oldMaxVerts = getField( %colData, 5 ); + %action.oldBoxMax = getField( %colData, 6 ); + %action.oldSphereMax = getField( %colData, 7 ); + %action.oldCapsuleMax = getField( %colData, 8 ); + + %action.newType = %type; + %action.newTarget = %target; + %action.newDepth = %depth; + %action.newMerge = %merge; + %action.newConcavity = %concavity; + %action.newMaxVerts = %maxVerts; + %action.newBoxMax = %boxMax; + %action.newSphereMax = %sphereMax; + %action.newCapsuleMax = %capsuleMax; + + %this.doAction( %action ); +} + +function ActionEditCollision::updateCollision( %this, %type, %target, %depth, %merge, %concavity, + %maxVerts, %boxMax, %sphereMax, %capsuleMax ) +{ + %colDetailSize = -1; + %colNode = "Col" @ %colDetailSize; + + // TreeView items are case sensitive, but TSShape names are not, so fixup case + // if needed + %index = ShapeEditor.shape.getNodeIndex( %colNode ); + if ( %index != -1 ) + %colNode = ShapeEditor.shape.getNodeName( %index ); + + // First remove the old detail and collision nodes + %meshList = ShapeEditor.getDetailMeshList( %colDetailSize ); + %meshCount = getFieldCount( %meshList ); + if ( %meshCount > 0 ) + { + ShapeEditor.shape.removeDetailLevel( %colDetailSize ); + for ( %i = 0; %i < %meshCount; %i++ ) + ShapeEdPropWindow.update_onMeshRemoved( getField( %meshList, %i ) ); + } + + %nodeList = ShapeEditor.getNodeNames( %colNode, "" ); + %nodeCount = getFieldCount( %nodeList ); + if ( %nodeCount > 0 ) + { + for ( %i = 0; %i < %nodeCount; %i++ ) + ShapeEditor.shape.removeNode( getField( %nodeList, %i ) ); + ShapeEdPropWindow.update_onNodeRemoved( %nodeList, %nodeCount ); + } + + // Add the new node and geometry + if ( %type $= "" ) + return; + + if ( !ShapeEditor.shape.addCollisionDetail( %colDetailSize, %type, %target, + %depth, %merge, %concavity, %maxVerts, + %boxMax, %sphereMax, %capsuleMax ) ) + return false; + + // Update UI + %meshList = ShapeEditor.getDetailMeshList( %colDetailSize ); + ShapeEdPropWindow.update_onNodeAdded( %colNode, ShapeEditor.shape.getNodeCount() ); // will also add child nodes + %count = getFieldCount( %meshList ); + for ( %i = 0; %i < %count; %i++ ) + ShapeEdPropWindow.update_onMeshAdded( getField( %meshList, %i ) ); + + ShapeEdColWindow.lastColSettings = %type TAB %target TAB %depth TAB %merge TAB + %concavity TAB %maxVerts TAB %boxMax TAB %sphereMax TAB %capsuleMax; + ShapeEdColWindow.update_onCollisionChanged(); + + return true; +} + +function ActionEditCollision::doit( %this ) +{ + ShapeEdWaitGui.show( "Generating collision geometry..." ); + %success = %this.updateCollision( %this.newType, %this.newTarget, %this.newDepth, %this.newMerge, + %this.newConcavity, %this.newMaxVerts, %this.newBoxMax, + %this.newSphereMax, %this.newCapsuleMax ); + ShapeEdWaitGui.hide(); + + return %success; +} + +function ActionEditCollision::undo( %this ) +{ + Parent::undo( %this ); + + ShapeEdWaitGui.show( "Generating collision geometry..." ); + %this.updateCollision( %this.oldType, %this.oldTarget, %this.oldDepth, %this.oldMerge, + %this.oldConcavity, %this.oldMaxVerts, %this.oldBoxMax, + %this.oldSphereMax, %this.oldCapsuleMax ); + ShapeEdWaitGui.hide(); +} + +//------------------------------------------------------------------------------ +// Remove Detail + +function ShapeEditor::doRemoveDetail( %this, %size ) +{ + %action = %this.createAction( ActionRemoveDetail, "Remove detail level" ); + %action.size = %size; + + %this.doAction( %action ); +} + +function ActionRemoveDetail::doit( %this ) +{ + %meshList = ShapeEditor.getDetailMeshList( %this.size ); + if ( ShapeEditor.shape.removeDetailLevel( %this.size ) ) + { + %meshCount = getFieldCount( %meshList ); + for ( %i = 0; %i < %meshCount; %i++ ) + ShapeEdPropWindow.update_onMeshRemoved( getField( %meshList, %i ) ); + return true; + } + return false; +} + +function ActionRemoveDetail::undo( %this ) +{ + Parent::undo( %this ); +} + +//------------------------------------------------------------------------------ +// Update bounds +function ShapeEditor::doSetBounds( %this ) +{ + %action = %this.createAction( ActionSetBounds, "Set bounds" ); + %action.oldBounds = ShapeEditor.shape.getBounds(); + %action.newBounds = ShapeEdShapeView.computeShapeBounds(); + + %this.doAction( %action ); +} + +function ActionSetBounds::doit( %this ) +{ + return ShapeEditor.shape.setBounds( %this.newBounds ); +} + +function ActionSetBounds::undo( %this ) +{ + Parent::undo( %this ); + + ShapeEditor.shape.setBounds( %this.oldBounds ); +} + +//------------------------------------------------------------------------------ +// Add/edit imposter +function ShapeEditor::doEditImposter( %this, %dl, %detailSize, %bbEquatorSteps, %bbPolarSteps, + %bbDetailLevel, %bbDimension, %bbIncludePoles, %bbPolarAngle ) +{ + %action = %this.createAction( ActionEditImposter, "Edit imposter" ); + %action.oldDL = %dl; + if ( %action.oldDL != -1 ) + { + %action.oldSize = ShapeEditor.shape.getDetailLevelSize( %dl ); + %action.oldImposter = ShapeEditor.shape.getImposterSettings( %dl ); + } + %action.newSize = %detailSize; + %action.newImposter = "1" TAB %bbEquatorSteps TAB %bbPolarSteps TAB %bbDetailLevel TAB + %bbDimension TAB %bbIncludePoles TAB %bbPolarAngle; + + %this.doAction( %action ); +} + +function ActionEditImposter::doit( %this ) +{ + // Unpack new imposter settings + for ( %i = 0; %i < 7; %i++ ) + %val[%i] = getField( %this.newImposter, %i ); + + ShapeEdWaitGui.show( "Generating imposter bitmaps..." ); + + // Need to de-highlight the current material, or the imposter will have the + // highlight effect baked in! + ShapeEdMaterials.updateSelectedMaterial( false ); + + %dl = ShapeEditor.shape.addImposter( %this.newSize, %val[1], %val[2], %val[3], %val[4], %val[5], %val[6] ); + ShapeEdWaitGui.hide(); + + // Restore highlight effect + ShapeEdMaterials.updateSelectedMaterial( ShapeEdMaterials-->highlightMaterial.getValue() ); + + if ( %dl != -1 ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.currentDL = %dl; + ShapeEdAdvancedWindow-->detailSize.setText( %this.newSize ); + ShapeEdDetails-->meshSize.setText( %this.newSize ); + ShapeEdDetails.update_onDetailsChanged(); + + return true; + } + return false; +} + +function ActionEditImposter::undo( %this ) +{ + Parent::undo( %this ); + + // If this was a new imposter, just remove it. Otherwise restore the old settings + if ( %this.oldDL < 0 ) + { + if ( ShapeEditor.shape.removeImposter() ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.currentDL = 0; + ShapeEdDetails.update_onDetailsChanged(); + } + } + else + { + // Unpack old imposter settings + for ( %i = 0; %i < 7; %i++ ) + %val[%i] = getField( %this.oldImposter, %i ); + + ShapeEdWaitGui.show( "Generating imposter bitmaps..." ); + + // Need to de-highlight the current material, or the imposter will have the + // highlight effect baked in! + ShapeEdMaterials.updateSelectedMaterial( false ); + + %dl = ShapeEditor.shape.addImposter( %this.oldSize, %val[1], %val[2], %val[3], %val[4], %val[5], %val[6] ); + ShapeEdWaitGui.hide(); + + // Restore highlight effect + ShapeEdMaterials.updateSelectedMaterial( ShapeEdMaterials-->highlightMaterial.getValue() ); + + if ( %dl != -1 ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.currentDL = %dl; + ShapeEdAdvancedWindow-->detailSize.setText( %this.oldSize ); + ShapeEdDetails-->meshSize.setText( %this.oldSize ); + } + } +} + +//------------------------------------------------------------------------------ +// Remove imposter +function ShapeEditor::doRemoveImposter( %this ) +{ + %action = %this.createAction( ActionRemoveImposter, "Remove imposter" ); + %dl = ShapeEditor.shape.getImposterDetailLevel(); + if ( %dl != -1 ) + { + %action.oldSize = ShapeEditor.shape.getDetailLevelSize( %dl ); + %action.oldImposter = ShapeEditor.shape.getImposterSettings( %dl ); + %this.doAction( %action ); + } +} + +function ActionRemoveImposter::doit( %this ) +{ + if ( ShapeEditor.shape.removeImposter() ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.currentDL = 0; + ShapeEdDetails.update_onDetailsChanged(); + + return true; + } + return false; +} + +function ActionRemoveImposter::undo( %this ) +{ + Parent::undo( %this ); + + // Unpack the old imposter settings + for ( %i = 0; %i < 7; %i++ ) + %val[%i] = getField( %this.oldImposter, %i ); + + ShapeEdWaitGui.show( "Generating imposter bitmaps..." ); + %dl = ShapeEditor.shape.addImposter( %this.oldSize, %val[1], %val[2], %val[3], %val[4], %val[5], %val[6] ); + ShapeEdWaitGui.hide(); + + if ( %dl != -1 ) + { + ShapeEdShapeView.refreshShape(); + ShapeEdShapeView.currentDL = %dl; + ShapeEdAdvancedWindow-->detailSize.setText( %this.oldSize ); + ShapeEdDetails-->meshSize.setText( %this.oldSize ); + ShapeEdDetails.update_onDetailsChanged(); + } +} diff --git a/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorHints.ed.cs b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorHints.ed.cs new file mode 100644 index 000000000..c8a704644 --- /dev/null +++ b/Templates/BaseGame/game/tools/shapeEditor/scripts/shapeEditorHints.ed.cs @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// The Shape Editor tool can provide some simple hints about which nodes and +// sequences are required/desired for a particular type of shape to work with +// Torque. The following objects define the node and sequence hints, and the +// Shape Editor gui marks them as present or not-present in the current shape. + +new SimGroup (ShapeHintControls) +{ +}; + +new SimGroup (ShapeHintGroup) +{ + new ScriptObject() + { + objectType = "ShapeBase"; + node0 = "cam" TAB "This node is used as the 3rd person camera position."; + node1 = "eye" TAB "This node is used as the 1st person camera position."; + node2 = "ear" TAB "This node is where the SFX listener is mounted on (if missing, eye is used)."; + node3 = "mount0-31" TAB "Nodes used for mounting ShapeBaseImages to this object"; + node4 = "AIRepairNode" TAB "unused"; + sequence0 = "Visibility" TAB "2 frame sequence used to show object damage (start=no damage, end=fully damaged)"; + sequence1 = "Damage" TAB "Sequence used to show object damage (start=no damage, end=fully damaged)"; + }; + + new ScriptObject() + { + objectType = "ShapeBaseImageData"; + node0 = "ejectPoint" TAB "Node from which shell casings are emitted (for weapon ShapeImages)"; + node1 = "muzzlePoint" TAB "Node used to fire projectiles and particles (for weapon ShapeImages)"; + node2 = "retractionPoint" TAB "Nearest point to use as muzzle when up against a wall (and muzzle node is inside wall)"; + node3 = "mountPoint" TAB "Where to attach to on this object"; + sequence0 = "ambient" TAB "Cyclic sequence to play while image is mounted"; + sequence1 = "spin" TAB "Cyclic sequence to play while image is mounted"; + }; + + new ScriptObject() + { + objectType = "Player"; + node0 = "Bip01 Pelvis" TAB ""; + node1 = "Bip01 Spine" TAB ""; + node2 = "Bip01 Spine1" TAB ""; + node3 = "Bip01 Spine2" TAB ""; + node4 = "Bip01 Neck" TAB ""; + node5 = "Bip01 Head" TAB ""; + + sequence0 = "head" TAB "Vertical head movement (for looking) (start=full up, end=full down)"; + sequence1 = "headside" TAB "Horizontal head movement (for looking) (start=full left, end=full right)"; + sequence2 = "look" TAB "Vertical arm movement (for looking) (start=full up, end=full down)"; + sequence3 = "light_recoil" TAB "Player has been hit lightly"; + sequence4 = "medium_recoil" TAB "Player has been hit moderately hard"; + sequence5 = "heavy_recoil" TAB "Player has been hit hard"; + + sequence6 = "root" TAB "Player is not moving"; + sequence7 = "run" TAB "Player is running forward"; + sequence8 = "back" TAB "Player is running backward"; + sequence9 = "side" TAB "Player is running sideways left (strafing)"; + sequence9 = "side_right" TAB "Player is running sideways right (strafing)"; + + sequence10 = "crouch_root" TAB "Player is crouched and not moving"; + sequence11 = "crouch_forward" TAB "Player is crouched and moving forward"; + sequence11 = "crouch_backward" TAB "Player is crouched and moving backward"; + sequence11 = "crouch_side" TAB "Player is crouched and moving sideways left"; + sequence11 = "crouch_right" TAB "Player is crouched and moving sideways right"; + + sequence12 = "prone_root" TAB "Player is lying down and not moving"; + sequence13 = "prone_forward" TAB "Player is lying down and moving forward"; + sequence13 = "prone_backward" TAB "Player is lying down and moving backward"; + + sequence14 = "swim_root" TAB "Player is swimming and not moving"; + sequence15 = "swim_forward" TAB "Player is swimming and moving forward"; + sequence16 = "swim_backward" TAB "Player is swimming and moving backward"; + sequence17 = "swim_left" TAB "Player is swimming and moving left"; + sequence18 = "swim_right" TAB "Player is swimming and moving right"; + + sequence19 = "fall" TAB "Player is falling"; + sequence20 = "jump" TAB "Player has jumped from a moving start"; + sequence21 = "standjump" TAB "Player has jumped from a standing start"; + sequence22 = "land" TAB "Player has landed after falling"; + sequence23 = "jet" TAB "Player is jetting"; + + sequence24 = "death1-11" TAB "Player has been killed (only one of these will play)"; + }; + + new ScriptObject() + { + objectType = "WheeledVehicle"; + node0 = "hub0-7" TAB "Placement node for wheel X"; + sequence0 = "spring0-7" TAB "Spring suspension for wheel X (start=fully compressed, end=fully extended)"; + sequence1 = "steering" TAB "Steering mechanism (start=full left, end=full right)"; + sequence2 = "brakelight" TAB "Sequence to play when braking (start=brakes off, end=brakes on)"; + }; + + new ScriptObject() + { + objectType = "HoverVehicle"; + node0 = "JetNozzle0-3" TAB "Nodes for jet engine exhaust particle emission"; + node1 = "JetNozzleX" TAB "Nodes for jet engine exhaust particle emission"; + sequence0 = "activateBack" TAB "Non-cyclic sequence to play when vehicle first starts moving backwards"; + sequence1 = "maintainBack" TAB "Cyclic sequence to play when vehicle continues moving backwards"; + }; + + new ScriptObject() + { + objectType = "FlyingVehicle"; + node0 = "JetNozzle0-3" TAB "Nodes for jet engine exhaust particle emission"; + node1 = "JetNozzleX" TAB "Nodes for jet engine exhaust particle emission"; + node2 = "JetNozzleY" TAB "Nodes for jet engine exhaust particle emission"; + node3 = "contrail0-3" TAB "Nodes for contrail particle emission"; + sequence0 = "activateBack" TAB "Sequence to play when vehicle first starts thrusting backwards"; + sequence1 = "maintainBack" TAB "Cyclic sequence to play when vehicle continues thrusting backwards"; + sequence2 = "activateBot" TAB "Non-cyclic sequence to play when vehicle first starts thrusting upwards"; + sequence3 = "maintainBot" TAB "Cyclic sequence to play when vehicle continues thrusting upwards"; + }; + + new ScriptObject() + { + objectType = "Projectile"; + sequence0 = "activate" TAB "Non-cyclic sequence to play when projectile is first created"; + sequence1 = "maintain" TAB "Cyclic sequence to play for remainder of projectile lifetime"; + }; +}; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/AddFMODProjectDlg.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/AddFMODProjectDlg.ed.gui new file mode 100644 index 000000000..56c892fa9 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/AddFMODProjectDlg.ed.gui @@ -0,0 +1,284 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(AddFMODProjectDlg,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "AddFMODProjectDlg.onCancel();"; + EdgeSnap = "1"; + text = "Add FMOD Designer Audio"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "361 196"; + Extent = "303 236"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + internalName = "window"; + + new GuiBitmapBorderCtrl() { + isContainer = "1"; + Profile = "ToolsGuiGroupBorderProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 26"; + Extent = "291 160"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Path to the compiled event file (.fev):"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 51"; + Extent = "176 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 72"; + Extent = "254 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "fileNameField"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 127"; + Extent = "254 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "mediaPathField"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Name for the SFXFMODProject object:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 10"; + Extent = "189 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 30"; + Extent = "277 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "projectNameField"; + canSaveDynamicFields = "0"; + }; + new GuiMLTextCtrl() { + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + text = "Path to the project\'s media files (leave empty if files are in same directory as the project file):"; + useURLMouseCursor = "0"; + isContainer = "0"; + Profile = "ToolsGuiMLTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 96"; + Extent = "276 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "266 72"; + Extent = "18 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + command = "AddFMODProjectDlg.onSelectFile();"; + }; + new GuiButtonCtrl() { + text = "..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "266 127"; + Extent = "18 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + command = "AddFMODProjectDlg.onSelectMediaPath();"; + }; + }; + new GuiButtonCtrl() { + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "206 196"; + Extent = "90 30"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "AddFMODProjectDlg.onCancel();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "112 196"; + Extent = "90 30"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "AddFMODProjectDlg.onOK();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/AxisGizmoSettingsTab.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/AxisGizmoSettingsTab.ed.gui new file mode 100644 index 000000000..e5eba667e --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/AxisGizmoSettingsTab.ed.gui @@ -0,0 +1,524 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(AxisGizmoSettingsTab,EditorGuiGroup) { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EAxisGizmoSettingsPage) { + fitBook = "1"; + text = "Axis Gizmo"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiSolidDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "208 568"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "208 568"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Gizmo"; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "-1 0"; + Extent = "208 79"; + Docking = "none"; + + new GuiTextCtrl() { + text = "Rotate Scalar:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 6"; + extent = "70 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "0.8"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "81 5"; + extent = "121 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/mouseRotateScalar"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiTextCtrl() { + text = "Scale Scalar:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 26"; + extent = "70 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "0.8"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "81 24"; + extent = "121 17"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/mouseScaleScalar"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Render When Manipulated"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 44"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/renderWhenUsed"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Render Tool Text"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 61"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/renderInfoText"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Grid"; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "-1 0"; + Extent = "208 82"; + Docking = "none"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Render Plane"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 4"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/Grid/renderPlane"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Render Plane Hashes"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 21"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/Grid/renderPlaneHashes"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiTextCtrl() { + text = "Plane Size:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 40"; + extent = "70 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + text = "500"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "81 38"; + extent = "121 17"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/Grid/planeDim"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "5 58"; + extent = "208 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "AxisGizmo/Grid/gridColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Plane Color:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 2"; + extent = "70 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "76 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "0 0 0 0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "184 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/CameraSettingsTab.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/CameraSettingsTab.ed.gui new file mode 100644 index 000000000..f6018ea9f --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/CameraSettingsTab.ed.gui @@ -0,0 +1,429 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(CameraSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(ECameraSettingsPage) { + fitBook = "1"; + text = "Camera Settings"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "245 568"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "245 568"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + internalName = "content"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "245 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Mouse Control"; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "-1 0"; + Extent = "208 41"; + Docking = "none"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Invert Y Axis"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 5"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "Camera/invertYAxis"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Invert X Axis"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 22"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "Camera/invertXAxis"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +function ECameraSettingsPage::init(%this) +{ + %this.currentLevel = ""; + %this.currentRolloutCtrl = ""; + + %levelInfoPath = "LevelInformation/levels"; + for( %fieldName = EditorSettings.findFirstValue(%levelInfoPath, true, true); + %fieldName !$= ""; + %fieldName = EditorSettings.findNextValue() ) + { + %fieldSlashPos = 0; + %levelSlashPos = 0; + while( strpos( %fieldName, "/", %fieldSlashPos ) != -1 ) + { + %levelSlashPos = %fieldSlashPos; + + %temp = strpos( %fieldName, "/", %fieldSlashPos ); + %fieldSlashPos = %temp + 1; + } + %levelName = getSubStr( %fieldName , %levelSlashPos , ((%fieldSlashPos - %levelSlashPos) - 1)); + + for( %i = 0; %i < %this-->content.getCount(); %i++ ) + { + %alreadyExist = false; + if( %this-->content.getObject(%i).caption $= %levelName ) + { + %alreadyExist = true; + break; + } + } + + if( %this.currentLevel !$= %levelName && !%alreadyExist ) + { + //Hold current level and reset gui params + %this.currentLevel = %levelName; + //%this.currentLevel = "\""@%levelName@"\""; + + //Create and hold current rollout ctrl + %this.currentRolloutCtrl = new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = %levelName; + Margin = "0 0 0 -3"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiContainer(){ //spacer + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 2"; + }; + + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 22"; + Docking = "none"; + + new GuiTextCtrl() { + text = "Camera Speed Min:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 3"; + extent = "96 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "106 2"; + extent = "95 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readCameraSettings( \"" @ %levelName @ "\" );"; + editorSettingsValue = "LevelInformation/levels/" @ %levelName @ "/cameraSpeedMin"; + editorSettingsWrite = "EditorGui.writeCameraSettings( \"" @ %levelName @ "\" );"; // not in use + }; + }; + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 22"; + Docking = "none"; + + new GuiTextCtrl() { + text = "Camera Speed Max:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 3"; + extent = "96 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "106 2"; + extent = "95 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readCameraSettings( \"" @ %levelName @ "\" );"; + editorSettingsValue = "LevelInformation/levels/" @ %levelName @ "/cameraSpeedMax"; + editorSettingsWrite = "EditorGui.writeCameraSettings( \"" @ %levelName @ "\" );"; // not in use + }; + }; + new GuiContainer(){ + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "208 24"; + Docking = "none"; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "5 2"; + Extent = "196 18"; + MinExtent = "8 8"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ECameraSettingsPage.deleteCameraSettingsGroup(\"" @ %levelName @ "\", $ThisControl.getParent().getParent().getParent());"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Delete Level Settings"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "1"; + }; + }; + }; + }; + %this-->content.add(%this.currentRolloutCtrl); + } + } +} + +function ECameraSettingsPage::deleteCameraSettingsGroup( %this, %levelName, %rolloutCtrl ) +{ + if( %levelName $= EditorGui.levelName ) + { + MessageBoxOK("Error", "You may not delete the settings group associated with the currently loaded level"); + return; + } + + %levelInfoPath = "LevelInformation/levels/" @ %levelName; + for( %fieldName = EditorSettings.findFirstValue(%levelInfoPath, true, true); + %fieldName !$= ""; + %fieldName = EditorSettings.findNextValue() ) + { + EditorSettings.remove( %fieldName, true ); + } + + %rolloutCtrl.delete(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/EditorChooseLevelGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/EditorChooseLevelGui.ed.gui new file mode 100644 index 000000000..a384f59e7 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/EditorChooseLevelGui.ed.gui @@ -0,0 +1,269 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiContainer(EditorChooseLevelGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiContentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiChunkedBitmapCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiContentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "art/gui/background"; + useVariable = "0"; + tile = "0"; + }; +}; + +%guiContent = new GuiContainer(EditorChooseLevelContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiContentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl(EditorChooseLevelWindow) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "416 187"; + Extent = "192 393"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Level Selector"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 21"; + Extent = "171 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1: Edit an Existing Level"; + maxLength = "255"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "42 360"; + Extent = "107 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "WE_ReturnToMainMenu();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Play Game"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 38"; + Extent = "171 194"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "4 0"; + + new GuiMLTextCtrl(WE_LevelList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiMLTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 1"; + Extent = "148 70"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "1"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 250"; + Extent = "171 87"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = false; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "4 0"; + + new GuiMLTextCtrl(WE_TemplateList) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiMLTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 1"; + Extent = "148 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "1"; + }; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 232"; + Extent = "174 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "2: Create New from Template"; + maxLength = "255"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 338"; + Extent = "174 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "3: Play Game from Start"; + maxLength = "255"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui new file mode 100644 index 000000000..3eb8558e4 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/EditorGui.ed.gui @@ -0,0 +1,1304 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiContainer(EditorGui,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiContainer(EditorGuiToolbar) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "menubarProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 32"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + + new GuiBitmapButtonCtrl(EHWorldEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "4 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Open the WorldEditor"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/toolbar/world"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EHGuiEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "34 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleGuiEditor(true); $GuiEditorBtnPressed = true;"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Open the GuiEditor"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/toolbar/gui"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "64 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "Editor.close(\"PlayGui\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Play Game"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/toolbar/playbutton"; + groupNum = "0"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "98 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiBitmapButtonCtrl(EWorldEditorToggleCamera) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "102 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Camera Modes"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/toolbar/player"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = getWord(EWorldEditorToggleCamera.extent, 0)-6 SPC getWord(EWorldEditorToggleCamera.extent, 1)-6; + Extent = "4 4"; + MinExtent = "4 4"; + bitmap = "tools/gui/images/dropdown-button-arrow"; + }; + }; + new GuiControl(CameraSpeedDropdownContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "136 5"; + Extent = "136 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 6"; + Extent = "78 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Camera Speed"; + maxLength = "1024"; + }; + new GuiTextEditCtrl(EWorldEditorCameraSpeed) { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "78 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; + hovertime = "1000"; + text = "100"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "112 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(CameraSpeedDropdownCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the Camera Speed"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + /*new GuiPopUpMenuCtrl(EWorldEditorCameraSpeed) { + canSaveDynamicFields = "0"; + internalName = "CameraSpeedDropdown"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "136 7"; + Extent = "130 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + };*/ + new GuiBitmapButtonCtrl(visibilityToggleBtn) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "270 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "VisibilityDropdownToggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Visibility Modes (ALT V)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/visibility-toggle"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = getWord(visibilityToggleBtn.extent, 0)-6 SPC getWord(visibilityToggleBtn.extent, 1)-6; + Extent = "4 4"; + MinExtent = "4 4"; + bitmap = "tools/gui/images/dropdown-button-arrow"; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "303 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiPopUpMenuCtrl(EWorldEditorAlignPopup) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "439 2"; + Extent = "70 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + }; + + new GuiContainer(EditorGuiStatusBar) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "menubarProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "0 578"; + Extent = "800 22"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "Bottom"; + + new GuiTextCtrl(EWorldEditorStatusBarInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "3 2"; + Extent = "450 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Current Tool"; + maxLength = "255"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "459 2"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiTextCtrl(EWorldEditorStatusBarSelection) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "469 2"; + Extent = "180 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = ""; + maxLength = "255"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "659 2"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiPopUpMenuCtrl(EWorldEditorStatusBarCamera) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "667 2"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "800 2"; + Extent = "2 18"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + }; + + new WorldEditor(EWorldEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "WorldEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "255 0 0 20"; + missionAreaFrameColor = "255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "255 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + isDirty = "0"; + stickToGround = "0"; + dropAtBounds = "1"; + dropBelowCameraOffset = "15"; + dropType = "screenCenter"; + boundingBoxCollision = "1"; + renderPopupBackground = "1"; + popupBackgroundColor = "100 100 100 255"; + popupTextColor = "255 255 0 255"; + objectTextColor = "255 255 255 255"; + objectsUseBoxCenter = "1"; + objSelectColor = "255 0 0 255"; + objMouseOverSelectColor = "0 0 255 255"; + objMouseOverColor = "0 255 0 255"; + showMousePopupInfo = "1"; + dragRectColor = "255 255 0 255"; + renderObjText = "1"; + renderObjHandle = "1"; + objTextFormat = "$name|class$"; + faceSelectColor = "0 0 100 100"; + renderSelectionBox = "1"; + selectionBoxColor = "255 255 0 255"; + selectionLocked = "0"; + toggleIgnoreList = "0"; + selectHandle = "tools/worldEditor/images/SelectHandle.png"; + defaultHandle = "tools/worldEditor/images/DefaultHandle.png"; + lockedHandle = "tools/worldEditor/images/LockedHandle.png"; + }; + new TerrainEditor(ETerrainEditor) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "WorldEditorProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + cameraZRot = "0"; + forceFOV = "0"; + renderMissionArea = "0"; + missionAreaFillColor = "0 0 0 20";//"255 0 0 20"; + missionAreaFrameColor = "0 0 0 128";//"255 0 0 128"; + allowBorderMove = "0"; + borderMovePixelSize = "20"; + borderMoveSpeed = "0.1"; + consoleFrameColor = "0 0 0 255"; + consoleFillColor = "0 0 0 0"; + consoleSphereLevel = "1"; + consoleCircleSegments = "32"; + consoleLineWidth = "1"; + GizmoProfile = "GlobalGizmoProfile"; + isDirty = "0"; + isMissionDirty = "0"; + renderBorder = "1"; + borderHeight = "10"; + borderFillColor = "0 255 0 20"; + borderFrameColor = "0 255 0 128"; + borderLineMode = "0"; + selectionHidden = "1"; + renderVertexSelection = "1"; + processUsesBrush = "0"; + maxBrushSize = "256 256"; + adjustHeightVal = "10"; + setHeightVal = "100"; + scaleVal = "1"; + smoothFactor = "0.1"; + materialGroup = "0"; + softSelectRadius = "50"; + softSelectFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; + softSelectDefaultFilter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; + adjustHeightMouseScale = "0.1"; + paintIndex = "-1"; + + new GuiTextCtrl(TESelectionInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "288 549"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Selection) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TEMouseBrushInfo) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "40 549"; + Extent = "107 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Mouse) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TESelectionInfo1) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfileWhite"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "289 550"; + Extent = "120 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Selection) #: 0 avg: 0"; + maxLength = "255"; + }; + new GuiTextCtrl(TEMouseBrushInfo1) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfileWhite"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "41 550"; + Extent = "107 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " (Mouse) #: 0 avg: 0"; + maxLength = "255"; + }; + }; + + new GuiControl(RelightStatus) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "223 277"; + Extent = "353 45"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + + new GuiProgressBitmapCtrl(RelightProgress) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiRLProgressBitmapProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "5 0"; + Extent = "440 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + + }; + new GuiTextCtrl(RelightProgressTxt) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiProgressTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 20"; + Extent = "440 20"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Loading Mission"; + maxLength = "255"; + }; + }; + new GuiControl(RelightMessage) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "19 570"; + Extent = "583 23"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "5 1"; + Extent = "449 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "A lightmapped object has been altered; relight the scene!"; + maxLength = "255"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "468 2"; + Extent = "75 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Editor.lightScene(\"\", forceAlways); RelightMessage.visible = false;"; + hovertime = "1000"; + text = "Relight Scene"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "548 2"; + Extent = "32 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RelightMessage.visible = false;"; + hovertime = "1000"; + text = "Hide"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiControl(PhysicsEditMessage) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "center"; + VertSizing = "top"; + Position = "180 560"; + Extent = "440 32"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "width"; + VertSizing = "center"; + Position = "5 0"; + Extent = "238 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "PHYSICS SIMULATION PAUSED FOR EDITING..."; + maxLength = "255"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "337 3"; + Extent = "43 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "physicsStart(); PhysicsEditMessage.visible = false;"; + hovertime = "1000"; + text = "Start"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "392 3"; + Extent = "43 26"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "PhysicsEditMessage.visible = false;"; + hovertime = "1000"; + text = "Hide"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + new GuiContainer(CameraTypesDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(EWorldEditorToggleCamera.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "137" SPC ((6*28)+6);//97"; + isContainer = "1"; + visible = "0"; + + new GuiDynamicCtrlArrayControl(cameraDropdownArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "5 5"; + Extent = "132" SPC getWord(CameraTypesDropdown.extent, 1)-5; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "1"; + colSize = "127"; + rowCount = "0"; + RowSize = "64"; + rowSpacing = "3"; + colSpacing = "3"; + autoCellSize = "1"; + fillRowFirst = "0"; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "StandardCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Standard Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Free Camera"; + hovertime = "1000"; + iconBitmap = "tools/worldEditor/images/toolbar/camera_n"; + groupNum = "0"; + text="Free Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "OrbitCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 32"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Orbit Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Orbit Cam"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/orbit-cam_n"; + groupNum = "0"; + text="Orbit Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "PlayerCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"1st Person Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Player Camera"; + hovertime = "1000"; + iconBitmap = "tools/worldEditor/images/toolbar/player_n"; + groupNum = "0"; + text="Player Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "trdPersonCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"3rd Person Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "3rd Person Camera"; + hovertime = "1000"; + iconBitmap = "tools/worldEditor/images/toolbar/3rd-person-camera_n"; + groupNum = "0"; + text="3rd Person Cam"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "NewtonianCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 64"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Newtonian Cam"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/smooth-cam_n"; + groupNum = "0"; + text="Smooth Camera"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "NewtonianRotationCamera"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 64"; + Extent = "127 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "CameraTypesDropdownToggle(); EditorGuiStatusBar.setCamera(\"Smooth Rot Camera\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggle Smooth Camera with Smooth Rotation"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/smooth-cam-rot_n"; + groupNum = "0"; + text="Smooth Rotate"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + }; + + new GuiDecoyCtrl(CameraTypesDropdownDecoy) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = getWord(CameraTypesDropdown.extent, 0) SPC getWord(CameraTypesDropdown.extent, 1); + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + useMouseEvents = "1"; + isDecoy = "1"; + }; + + }; + new GuiContainer(VisibilityDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(visibilityToggleBtn.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "159 261"; //SPC ((6*28)+6);//97"; + isContainer = "1"; + visible = "0"; + + new GuiTabBookCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "3 3 3 3"; + Position = "5 24"; + Extent = "170 226"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + TabPosition = "Top"; + TabHeight = "22"; + TabMargin = "7"; + MinTabWidth = "64"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Viz Toggles"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theVisOptionsList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Class Toggles"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theClassVisList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + }; + }; +}; + + + + +new GuiMouseEventCtrl(CameraSpeedDropdownCtrlContainer, EditorGuiGroup) { + internalName = "AggregateControl"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiContainer(){ + position = firstWord(CameraSpeedDropdownContainer.position) + firstWord(EditorGuiToolbar.position) + -6 SPC + (getWord(CameraSpeedDropdownContainer, 1)) + 31; + extent = "146 39"; + isContainer = "1"; + Profile = "IconDropdownProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + + new GuiBitmapCtrl(){ // Fast + position = "105 15"; + extent = "2 8"; + bitmap = "tools/gui/images/separator-h.png"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiBitmapCtrl(){ // normal + position = "73 15"; + extent = "2 8"; + bitmap = "tools/gui/images/separator-h.png"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiBitmapCtrl(){ // slow + position = "41 15"; + extent = "2 8"; + bitmap = "tools/gui/images/separator-h.png"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + + new GuiSliderCtrl(){ //camera speed slider + internalName = "slider"; + position = "9 17"; + extent = "129 15"; + bitmap = "tools/gui/images/separator-h.png"; + HorizSizing = "width"; + VertSizing = "bottom"; + range = "1 200"; + ticks = "0"; + value = "100"; + AltCommand = "EWorldEditorCameraSpeed.updateMenuBar( $ThisControl );"; + }; + new GuiTextCtrl(){ // Normal + internalName = "text"; + position = "54 3"; + extent = "39 18"; + text = "Normal"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiTextCtrl(){ // - + position = "11 2"; + extent = "39 18"; + text = "-"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + new GuiTextCtrl(){ // + + position = "98 5"; + extent = "39 13"; + text = "+"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + }; + }; +}; +//--- OBJECT WRITE END --- \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/EditorSettingsWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/EditorSettingsWindow.ed.gui new file mode 100644 index 000000000..7662ecf00 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/EditorSettingsWindow.ed.gui @@ -0,0 +1,122 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(EditorSettingsWindow,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiWindowCollapseCtrl(ESettingsWindow) { + internalName = "EditorSettingsWindow"; + resizeWidth = "0"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Editor Settings"; + closeCommand = "ESettingsWindow.hideDialog();"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "30 200"; + Extent = "319 320"; + MinExtent = "319 100"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTabBookCtrl(ESettingsWindowTabBook) { + TabPosition = "Top"; + TabMargin = "7"; + MinTabWidth = "64"; + TabHeight = "0"; + AllowReorder = "0"; + FrontTabPadding = "0"; + Docking = "Client"; + Margin = "3 1 4 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookNoBitmapProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "201 21"; + Extent = "334 425"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "AlwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Docking = "Left"; + Margin = "3 1 3 -1"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 21"; + Extent = "100 425"; + MinExtent = "100 50"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextListCtrl(ESettingsWindowList) { + AllowMultipleSelections = "1"; + fitParentWidth = "1"; + isContainer = "0"; + Profile = "ToolsGuiListBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "100 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/GeneralSettingsTab.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/GeneralSettingsTab.ed.gui new file mode 100644 index 000000000..b85e78ae0 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/GeneralSettingsTab.ed.gui @@ -0,0 +1,284 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(GeneralSettingsTab,EditorGuiGroup) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EGeneralSettingsPage) { + fitBook = "1"; + text = "General Settings"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "432 568"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiSolidDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "0 0"; + extent = "432 568"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "height"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "430 41"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiRolloutCtrl() { + caption = "Paths"; + margin = "0 3 0 0"; + defaultHeight = "40"; + expanded = "1"; + clickCollapse = "1"; + hideHeader = "0"; + autoCollapseSiblings = "0"; + position = "0 0"; + extent = "430 41"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiRolloutProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "3"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "0 20"; + extent = "430 18"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiControl() { + position = "0 0"; + extent = "430 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "New Level"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 1"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "tools/levels/BlankRoom.mis"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "81 0"; + extent = "345 17"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/newLevelFile"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + new GuiControl() { + position = "0 0"; + extent = "430 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "New Game Objects"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "5 1"; + extent = "70 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextRightProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "scripts/server/gameObjects"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "81 0"; + extent = "345 17"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/newGameObjectDir"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/GenericPromptDialog.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/GenericPromptDialog.ed.gui new file mode 100644 index 000000000..ade528157 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/GenericPromptDialog.ed.gui @@ -0,0 +1,115 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(GenericPromptDialog) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + internalName = "GenericPromptWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "336 337"; + Extent = "352 93"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Error"; + closeCommand = "Canvas.popDialog(GenericPromptDialog);"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + internalName = "GenericPromptText"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiProgressTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "13 33"; + Extent = "328 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Cannot use the Terrain Editor without a terrain"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "center"; + VertSizing = "top"; + Position = "134 61"; + Extent = "88 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.popDialog(GenericPromptDialog);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "center"; + VertSizing = "top"; + Position = "230 61"; + Extent = "88 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + Command = "Canvas.popDialog(GenericPromptDialog);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ManageBookmarksWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ManageBookmarksWindow.ed.gui new file mode 100644 index 000000000..49ec1d65b --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ManageBookmarksWindow.ed.gui @@ -0,0 +1,145 @@ +%guiContent = new GuiControl(ManageBookmarksContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(EManageBookmarks) { + internalName = "ManageBookmarksWindow"; + Enabled = "1"; + isContainer = "1"; + profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + resizeWidth = "1"; + resizeHeight = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + position = "50 90"; + extent = "180 306"; + MinExtent = "120 130"; + text = "Camera Bookmark Manager"; + closeCommand = "EManageBookmarks.hideDialog();"; + EdgeSnap = "0"; + canCollapse = "0"; + visible = "0"; + Margin = "5 5 5 5"; + Padding = "5 5 5 5"; + + new GuiControl(){ + horizSizing = "width"; + vertSizing = "bottom"; + position = "4 23"; + extent = "170 20"; + //Docking = "Top"; + + new GuiTextCtrl() { + profile = "GuiCenterTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "1 2"; + extent = "24 16"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = "New"; + }; + new GuiTextEditCtrl(EAddBookmarkWindowName) { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "27 2"; + extent = "126 18"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + historySize = "0"; + }; + new GuiBitmapButtonCtrl(EAddBookmarkWindowOK) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "158 3"; + extent = "17 17"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + command = "ManageBookmarksContainer.onOK();"; + bitmap = "tools/gui/images/new"; + helpTag = "0"; + text = "Create"; + tooltip = "Create New Camera Bookmark"; + accelerator = "return"; + }; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 12"; + Extent = "300 200"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "26 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 2"; + + new GuiStackControl() { + internalName = "ManageBookmarksWindowStack"; + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "300 200"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; +}; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ManageSFXParametersWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ManageSFXParametersWindow.ed.gui new file mode 100644 index 000000000..29c55dc74 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ManageSFXParametersWindow.ed.gui @@ -0,0 +1,240 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ManageSFXParametersContainer,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + enabled = "1"; + isDecoy = "0"; + + new GuiWindowCollapseCtrl(EManageSFXParameters) { + CollapseGroup = "-1"; + CollapseGroupNum = "-1"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EManageSFXParameters.setVisible( false );"; + EdgeSnap = "0"; + text = "Audio Parameters"; + Margin = "5 5 5 5"; + Padding = "5 5 5 5"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + position = "49 68"; + Extent = "446 392"; + MinExtent = "120 130"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ManageSFXParametersWindow"; + canSaveDynamicFields = "0"; + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 23"; + Extent = "484 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Name"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 2"; + Extent = "29 17"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "36 2"; + Extent = "226 17"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "AddSFXParameterName"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/new"; + autoFit = "0"; + text = "Create"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "266 2"; + Extent = "17 17"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "EManageSFXParameters.createNewParameter( EManageSFXParameters-->AddSFXParameterName.getText() );"; + Accelerator = "return"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Create New SFX Parameter"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "325 1"; + Extent = "113 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EManageSFXParameters.initList( $ThisControl.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "SFXParameterFilter"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Filter"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "296 2"; + Extent = "24 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "2 2"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "4 46"; + Extent = "438 344"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "2"; + DynamicSize = "1"; + ChangeChildSizeToFit = "1"; + ChangeChildPosition = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "3 3"; + Extent = "419 10008"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "SFXParametersStack"; + canSaveDynamicFields = "0"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ObjectEditorSettingsTab.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ObjectEditorSettingsTab.ed.gui new file mode 100644 index 000000000..b8928eb7d --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ObjectEditorSettingsTab.ed.gui @@ -0,0 +1,1093 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ObjectEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(EObjectEditorSettingsPage) { + fitBook = "1"; + text = "Object Editor"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiSolidDefaultProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "208 568"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "1"; + profile = "ToolsGuiScrollProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "208 568"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Render"; + Margin = "4 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Object Icons"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 10"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Render/renderObjHandle"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Object Text"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 30"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Render/renderObjText"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Mouse Popup Info"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 50"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Render/showMousePopupInfo"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Popup Menu Background"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 70"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Render/renderPopupBackground"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Colors"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 90"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Grid/gridColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Grid Major:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 110"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Grid/gridMinorColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Grid Minor:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 130"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Grid/gridOriginColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Grid Origin:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 17"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 10"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Color/dragRectColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Drag Rect:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 30"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Color/objectTextColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Object Text:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 50"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Color/popupTextColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Popup Text:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 70"; + extent = "204 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColor"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Color/popupBackgroundColor"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + + new GuiTextCtrl() { + text = "Popup Back:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextRightProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 1"; + extent = "70 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "80 0"; + extent = "104 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorEdit"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorEdit"; + }; + new GuiSwatchButtonCtrl() { + color = "1 1 1 1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "188 2"; + extent = "14 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "ColorButton"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowColorButton"; + }; + }; + }; + }; + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Misc"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "210 14"; + + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Force Load DAE"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + profile = "ToolsGuiCheckBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 0"; + extent = "140 14"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + variable = "EWorldEditor.forceLoadDAE"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowCheckbox"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/forceLoadDAE"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 70"; + extent = "210 18"; + + new GuiTextCtrl() { + text = "Screen Center Scalar:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 1"; + extent = "110 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "120 0"; + extent = "80 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Tools/dropAtScreenCenterScalar"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 70"; + extent = "210 18"; + + new GuiTextCtrl() { + text = "Screen Center Max:"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 1"; + extent = "110 16"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "120 0"; + extent = "80 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + editorSettingsRead = "EditorGui.readWorldEditorSettings();"; + editorSettingsValue = "WorldEditor/Tools/dropAtScreenCenterMax"; + editorSettingsWrite = "EditorGui.writeWorldEditorSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ObjectSnapOptionsWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ObjectSnapOptionsWindow.ed.gui new file mode 100644 index 000000000..24f5c8172 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ObjectSnapOptionsWindow.ed.gui @@ -0,0 +1,871 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ObjectSnapOptionsContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(ESnapOptions) { + internalName = "SnapOptionsWindow"; + Enabled = "1"; + isContainer = "1"; + profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + resizeWidth = "0"; + resizeHeight = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + position = "400 31"; + extent =" 175 257"; + MinExtent = "175 130"; + text = "Snap Options"; + closeCommand = "ESnapOptions.hideDialog();"; + EdgeSnap = "0"; + canCollapse = "0"; + visible = "0"; + Margin = "5 5 5 5"; + Padding = "5 5 5 5"; + + new GuiTabBookCtrl(ESnapOptionsTabBook) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "5 52"; + Extent = "190 240"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Docking = "Client"; + Margin = "3 22 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "10"; + MinTabWidth = "8"; + + new GuiTabPageCtrl(ESnapOptionsTabTerrain) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Margin = "0 0 0 0"; + Position = "0 19"; + Extent = "190 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + text = "Terrain"; + maxLength = "255"; + command = "toggleSnappingOptions(\"terrain\");"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 2"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "190 90"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiDynamicCtrlArrayControl() { + canSaveDynamicFields = "0"; + internalName = "TerrainSnapAlignment"; + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 18"; + Extent = "190 72"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "2"; + colSize = "78"; + rowSize = "20"; + rowSpacing = "2"; + colSpacing = "2"; + dynamicSize = true; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "190 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " Alignment:"; + maxLength = "1024"; + }; + new GuiRadioCtrl() { + canSaveDynamicFields = "0"; + internalName = "NoAlignment"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiRadioProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"None\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "None"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negX"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"-X\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- X Axis"; + iconBitmap = "tools/gui/images/axis-icon_-x"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posX"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"+X\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ X Axis"; + iconBitmap = "tools/gui/images/axis-icon_x"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negY"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"-Y\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- Y Axis"; + iconBitmap = "tools/gui/images/axis-icon_-y"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posY"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"+Y\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ Y Axis"; + iconBitmap = "tools/gui/images/axis-icon_y"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negZ"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"-Z\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- Z Axis"; + iconBitmap = "tools/gui/images/axis-icon_-z"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posZ"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setTerrainSnapAlignment(\"+Z\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ Z Axis"; + iconBitmap = "tools/gui/images/axis-icon_z"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + new GuiCheckBoxCtrl() { + text = "Snap to object bounding box"; + groupNum = "1"; + useMouseEvents = "0"; + isContainer = "0"; + horizSizing = "right"; + vertSizing = "top"; + position = "4 249"; + extent = "165 24"; + minExtent = "8 8"; + visible = "1"; + active = "1"; + Variable = "EWorldEditor.dropAtBounds"; + Command = "EWorldEditor.dropAtBounds = $ThisControl.getValue();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + }; + }; + new GuiTabPageCtrl(ESnapOptionsTabSoft) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Margin = "0 0 0 0"; + Position = "0 19"; + Extent = "190 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + text = "Soft"; + maxLength = "255"; + command = "toggleSnappingOptions(\"soft\");"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "186 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 2"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "5"; + canSaveDynamicFields = "0"; + internalName = "theVisOptionsList"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "2 2"; + Extent = "190 190"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + Position = "0 0"; + Extent = "190 18"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "90 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Snap Size:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + internalName = "SnapSize"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "left"; + position = "136 0"; + Extent = "44 18"; + text ="2.0"; + maxLength = "6"; + AltCommand = "ESnapOptions.setSoftSnapSize();"; + }; + }; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + Position = "0 0"; + Extent = "190 90"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiDynamicCtrlArrayControl() { + canSaveDynamicFields = "0"; + internalName = "SoftSnapAlignment"; + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "190 90"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "2"; + colSize = "78"; + rowSize = "20"; + rowSpacing = "2"; + colSpacing = "2"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "190 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " Alignment:"; + maxLength = "1024"; + }; + new GuiRadioCtrl() { + canSaveDynamicFields = "0"; + internalName = "NoAlignment"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiRadioProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"None\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "None"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negX"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"-X\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- X Axis"; + iconBitmap = "tools/gui/images/axis-icon_-x"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posX"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"+X\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ X Axis"; + iconBitmap = "tools/gui/images/axis-icon_x"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negY"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"-Y\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- Y Axis"; + iconBitmap = "tools/gui/images/axis-icon_-y"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posY"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"+Y\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ Y Axis"; + iconBitmap = "tools/gui/images/axis-icon_y"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "negZ"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"-Z\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "- Z Axis"; + iconBitmap = "tools/gui/images/axis-icon_-z"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "posZ"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonSmallProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "40 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "ESnapOptions.setSoftSnapAlignment(\"+Z\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+ Z Axis"; + iconBitmap = "tools/gui/images/axis-icon_z"; + textMargin = "24"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + }; + }; + + new GuiCheckBoxCtrl(){ + internalName = "RenderSnapBounds"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "1 0"; + Extent = "190 18"; + text = "Render Snap Bounds"; + Command = "ESnapOptions.toggleRenderSnapBounds();"; + }; + + new GuiCheckBoxCtrl(){ + internalName = "RenderSnappedTriangle"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "1 0"; + Extent = "190 18"; + text = "Render Snapped Triangle"; + Command = "ESnapOptions.toggleRenderSnappedTriangle();"; + }; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + Position = "0 0"; + Extent = "190 18"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "110 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Backface Tolerance:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + internalName = "SnapBackfaceTolerance"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "left"; + position = "136 0"; + Extent = "44 18"; + text ="0.5"; + maxLength = "6"; + AltCommand = "ESnapOptions.getSoftSnapBackfaceTolerance();"; + }; + }; + }; + }; + }; + }; + new GuiCheckBoxCtrl() { + text = "Grid Snapping"; + groupNum = "1"; + useMouseEvents = "0"; + isContainer = "0"; + horizSizing = "right"; + vertSizing = "top"; + position = "4 231"; + extent = "95 24"; + minExtent = "8 8"; + visible = "1"; + active = "1"; + command = "toggleSnappingOptions(\"grid\");"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "GridSnapButton"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Size"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiDefaultProfile"; + horizSizing = "left"; + vertSizing = "top"; + position = "103 234"; + extent = "25 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + text = "2.0"; + maxLength = "6"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + isContainer = "0"; + profile = "ToolsGuiNumericTextEditProfile"; + horizSizing = "left"; + vertSizing = "top"; + position = "127 235"; + extent = "44 18"; + minExtent = "8 2"; + visible = "1"; + active = "1"; + altCommand = "ESnapOptions.setGridSnapSize();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "gridSize"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + internalName = "NoSnapButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "133 23"; + Extent = "38 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleSnappingOptions(\"\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Off"; + groupNum = "1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui new file mode 100644 index 000000000..c00e31e4f --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui @@ -0,0 +1,405 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ProceduralTerrainPainterGui) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl(ProceduralTerrainPainterDescription) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "285 83"; + Extent = "175 233"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + canCollapse = "0"; + CollapseGroup = "-1"; + CollapseGroupNum = "-1"; + closeCommand = "Canvas.popDialog(ProceduralTerrainPainterGui);"; + text = "Generate layer mask"; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "19 193"; + Extent = "140 30"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "generateProceduralTerrainMask();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Generate"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "15 37"; + Extent = "33 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "HEIGHT"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "59 37"; + Extent = "23 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Min."; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "59 62"; + Extent = "23 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Max."; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "97 35"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = "$TPPHeightMin"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "97 60"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = "$TPPHeightMax"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "15 101"; + Extent = "33 13"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "SLOPE"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "59 101"; + Extent = "23 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Min."; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "59 126"; + Extent = "23 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Max."; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "97 99"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = "$TPPSlopeMin"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "97 124"; + Extent = "66 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = "$TPPSlopeMax"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + + + new GuiTextCtrl() { + text = "COVERAGE"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 165"; + extent = "55 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "97 162"; + extent = "66 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + variable = "$TPPCoverage"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "77 164"; + extent = "11 14"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +$TPPHeightMin = -10000; +$TPPHeightMax = 10000; +$TPPSlopeMin = 0; +$TPPSlopeMax = 90; +$TPPCoverage = 100; + +function autoLayers() +{ + Canvas.pushDialog(ProceduralTerrainPainterGui); +} + +function generateProceduralTerrainMask() +{ + Canvas.popDialog(ProceduralTerrainPainterGui); + ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax, $TPPCoverage); +} + diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/SelectObjectsWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/SelectObjectsWindow.ed.gui new file mode 100644 index 000000000..3e95e91d4 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/SelectObjectsWindow.ed.gui @@ -0,0 +1,566 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ESelectObjectsWindowContainer,EditorGuiGroup) { + position = "0 0"; + extent = "1024 768"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl(ESelectObjectsWindow) { + text = "Select Objects"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + canCollapse = "1"; + closeCommand = "$ThisControl.toggleVisibility();"; + edgeSnap = "1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "268 177"; + extent = "380 373"; + minExtent = "200 100"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiWindowProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + class = "EObjectSelection"; + internalName = "SelectObjectsWindow"; + + new GuiBitmapBorderCtrl() { + position = "7 104"; + extent = "265 262"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 25"; + extent = "246 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "246 1242"; + minExtent = "16 16"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "classList"; + canSave = "1"; + canSaveDynamicFields = "0"; + + }; + }; + new GuiButtonCtrl() { + text = "Select All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "10 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ESelectObjectsWindow.selectAllInClassList( true );"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Classes"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "113 6"; + extent = "40 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "76 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ESelectObjectsWindow.selectAllInClassList( false );"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + position = "7 25"; + extent = "366 74"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Name Pattern"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 9"; + extent = "67 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Retain Current Selection"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "216 46"; + extent = "140 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "retainSelection"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Create Selection Set"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "13 73"; + extent = "117 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiCheckBoxProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "createSelectionSet"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "•"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "157 80"; + extent = "199 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiTextEditProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "selectionSetName"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "•"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "91 9"; + extent = "265 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "namePattern"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Select Objects in Group"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "11 30"; + extent = "116 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "138 30"; + extent = "218 17"; + minExtent = "20 2"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiPopUpMenuProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "groupList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiBitmapBorderCtrl() { + position = "246 104"; + extent = "233 262"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiGroupBorderProfile"; + visible = "0"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "0"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "9 25"; + extent = "215 200"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiScrollProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + stackingType = "Vertical"; + horizStacking = "Left to Right"; + vertStacking = "Top to Bottom"; + padding = "0"; + dynamicSize = "1"; + changeChildSizeToFit = "1"; + changeChildPosition = "1"; + position = "1 1"; + extent = "215 16"; + minExtent = "16 16"; + horizSizing = "width"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + internalName = "filterList"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "Select All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "9 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Filters"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "101 6"; + extent = "30 17"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiAutoSizeTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect All"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "75 231"; + extent = "65 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + new GuiButtonCtrl() { + text = "Select"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "278 104"; + extent = "95 30"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ESelectObjectsWindow.onSelectObjects(true);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Deselect"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "278 137"; + extent = "95 30"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + fixedAspectRatio = "0"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "ESelectObjectsWindow.onSelectObjects(false);"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui new file mode 100644 index 000000000..6fb188adf --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui @@ -0,0 +1,232 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainBrushSoftnessCurveDlg, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "231 204"; + Extent = "175 228"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "0"; + text = "Brush Softness Curve"; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "84 196"; + Extent = "83 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "TerrainBrushSoftnessCurveDlg.onOk();"; + hovertime = "1000"; + text = "Ok"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiFilterCtrl() { + canSaveDynamicFields = "0"; + internalName = "FilterCurveCtrl"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "35 46"; + Extent = "130 128"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + controlPoints = "7"; + filter = "1 0.833333 0.666667 0.5 0.333333 0.166667 0"; + identity = "1 0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 43"; + Extent = "22 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Hard"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 159"; + Extent = "20 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Soft"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "36 174"; + Extent = "33 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Inside"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "129 176"; + Extent = "39 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Outside"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 196"; + Extent = "69 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.popDialog( TerrainBrushSoftnessCurveDlg );"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "122 26"; + Extent = "44 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainBrushSoftnessCurveDlg.resetCurve();"; + hovertime = "1000"; + text = "Reset"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + + +function TerrainBrushSoftnessCurveDlg::onWake( %this ) +{ + %curve = %this-->FilterCurveCtrl; + %curve.setValue( ETerrainEditor.softSelectFilter ); +} + +function TerrainBrushSoftnessCurveDlg::onOk( %this ) +{ + %curve = %this-->FilterCurveCtrl; + ETerrainEditor.softSelectFilter = %curve.getValue(); + ETerrainEditor.resetSelWeights(true); + + Canvas.popDialog( %this ); +} + +function TerrainBrushSoftnessCurveDlg::resetCurve( %this ) +{ + %curve = %this-->FilterCurveCtrl; + %curve.identity(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditToolbar.ed.gui new file mode 100644 index 000000000..cc9d6983b --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditToolbar.ed.gui @@ -0,0 +1,615 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(EWTerrainEditToolbar,EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "TerrainEditToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 7"; + extent = "70 16"; + minExtent = "8 8"; + visible = "1"; + text = "Brush Settings"; + maxLength = "255"; + helpTag = "0"; + }; + + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "760 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiControl(EWTerrainEditToolbarBrushType){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "83 2"; + Extent = "94 27"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "ellipse"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Circle Brush (V)"; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/circleBrush"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "box"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "31 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Box Brush (B)"; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/boxBrush"; + }; + + /* + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "selection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "62 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles the brush type."; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/maskBrush"; + }; + */ + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "152 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(TerrainBrushSizeTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "145 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "21 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Size"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + HorizSizing = "right"; + profile="ToolsGuiNumericDropSliderTextProfile"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setBrushSize( $ThisControl.getText() );"; + validate = "TerrainEditorPlugin.validateBrushSize();"; + hovertime = "1000"; + text = "9"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(TerrainBrushSizeSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes size of the brush (CTRL + Mouse Wheel)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "272 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(TerrainBrushPressureTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "287 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Pressure"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setBrushPressure( ($ThisControl.getValue() / 100) );"; + hovertime = "1000"; + text = "100"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(TerrainBrushPressureSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the pressure (CTRL + SHIFT + Mouse Wheel)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "412 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(TerrainBrushSoftnessTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "429 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Softness"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + HorizSizing = "right"; + profile="ToolsGuiNumericDropSliderTextProfile"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setBrushSoftness( ($ThisControl.getValue() / 100) );"; + hovertime = "1000"; + text = "1"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(TerrainBrushSoftnessSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the softness (SHIFT + Mouse Wheel)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "547 3"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog( TerrainBrushSoftnessCurveDlg );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the softness curve"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/softCurve"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "589 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(TerrainSetHeightTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "605 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "33 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Height"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + HorizSizing = "right"; + profile="ToolsGuiNumericDropSliderTextProfile"; + VertSizing = "bottom"; + position = "34 2"; + Extent = "62 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setHeightVal = $ThisControl.getValue();"; + hovertime = "1000"; + text = "1"; + maxLength = "7"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "88 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(TerrainSetHeightSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the height for the SetHeight tool (ALT + Left Mouse)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + }; +}; + +new GuiMouseEventCtrl(TerrainBrushSizeSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(TerrainBrushSizeTextEditContainer.position) + firstWord(EWTerrainEditToolbar.position)+11 SPC + (getWord(TerrainBrushSizeTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "TerrainBrushSizeTextEditContainer-->textEdit.setValue(mCeil($ThisControl.getValue())); ETerrainEditor.setBrushSize( $ThisControl.value );"; + range = "1 256"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(TerrainBrushPressureSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(TerrainBrushPressureTextEditContainer.position) + firstWord(EWTerrainEditToolbar.position) SPC + (getWord(TerrainBrushPressureTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "TerrainBrushPressureTextEditContainer-->textEdit.setValue( mCeil(100 * $ThisControl.getValue()) @ \"%\"); ETerrainEditor.setBrushPressure( $ThisControl.value );"; + range = "0.01 1"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(TerrainBrushSoftnessSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(TerrainBrushSoftnessTextEditContainer.position) + firstWord(EWTerrainEditToolbar.position) SPC + (getWord(TerrainBrushSoftnessTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "TerrainBrushSoftnessTextEditContainer-->textEdit.setValue( mCeil(100 * $ThisControl.getValue()) @ \"%\"); ETerrainEditor.setBrushSoftness( $ThisControl.value );"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(TerrainSetHeightSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(TerrainSetHeightTextEditContainer.position) + firstWord(EWTerrainEditToolbar.position) SPC + (getWord(TerrainSetHeightTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "TerrainSetHeightTextEditContainer-->textEdit.setValue( $ThisControl.getValue() ); ETerrainEditor.setHeightVal = $ThisControl.getValue();"; + range = "0 2047"; + ticks = "0"; + value = "100"; + }; +}; +//--- OBJECT WRITE END --- \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorSettingsTab.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorSettingsTab.ed.gui new file mode 100644 index 000000000..8f219ec67 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorSettingsTab.ed.gui @@ -0,0 +1,301 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainEditorSettingsTab,EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiTabPageCtrl(ETerrainEditorSettingsPage) { + fitBook = "1"; + text = "Terrain Editor"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiScrollCtrl() { + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "1"; + lockVertScroll = "0"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "208 400"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 1"; + extent = "208 210"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiRolloutCtrl() { + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "10 10"; + extent = "208 95"; + Caption = "Tool Values"; + Margin = "0 3 0 0"; + DragSizable = false; + container = true; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "208 0"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + padding = "3"; + + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Raise/Lower Height:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ETerrainEditor.adjustHeightVal = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "TerrainEditor/ActionValues/adjustHeightVal"; + editorSettingsWrite = "EditorGui.writeTerrainEditorSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Smooth Factor:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ETerrainEditor.smoothFactor = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "TerrainEditor/ActionValues/smoothFactor"; + editorSettingsWrite = "EditorGui.writeTerrainEditorSettings();"; + }; + }; + new GuiControl() { + isContainer = "1"; + horizSizing = "right"; + vertSizing = "bottom"; + extent = "208 18"; + + new GuiTextCtrl() { + text = "Noise Factor:"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextRightProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "5 1"; + Extent = "70 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "81 0"; + Extent = "121 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + class = "ESettingsWindowTextEdit"; + className = "ESettingsWindowTextEdit"; + editorSettingsRead = "ETerrainEditor.noiseFactor = EditorSettings.value(%this.editorSettingsValue);"; + editorSettingsValue = "TerrainEditor/ActionValues/noiseFactor"; + editorSettingsWrite = "EditorGui.writeTerrainEditorSettings();"; + }; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorVSettingsGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorVSettingsGui.ed.gui new file mode 100644 index 000000000..0a046a402 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainEditorVSettingsGui.ed.gui @@ -0,0 +1,276 @@ +//--- OBJECT WRITE BEGIN --- +new GuiControl(TerrainEditorValuesSettingsGui, EditorGuiGroup) { + profile = "ToolsGuiOverlayProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiWindowCtrl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "117 113"; + extent = "408 247"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Terrain Action Values"; + maxLength = "255"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "Canvas.popDIalog(TerrainEditorValuesSettingsGui);"; + + new GuiControl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "198 27"; + extent = "203 115"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiTextEditCtrl() { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "86 12"; + extent = "107 18"; + minExtent = "8 8"; + visible = "1"; + variable = "ETerrainEditor.adjustHeightVal"; + command = "ETerrainEditor.adjustHeightVal = $ThisControl.getValue();"; + helpTag = "0"; + maxLength = "255"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Adjust height increment."; + }; + new GuiTextEditCtrl() { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "86 62"; + extent = "107 18"; + minExtent = "8 8"; + visible = "1"; + variable = "ETerrainEditor.scaleVal"; + command = "ETerrainEditor.scaleVal = $ThisControl.getValue();"; + helpTag = "0"; + maxLength = "255"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Scale height increment."; + }; + new GuiTextEditCtrl() { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "86 87"; + extent = "107 18"; + minExtent = "8 8"; + visible = "1"; + variable = "ETerrainEditor.smoothFactor"; + command = "ETerrainEditor.smoothFactor = $ThisControl.getValue();"; + helpTag = "0"; + maxLength = "255"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Smoothing factor -- lower values are less agressive."; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "11 12"; + extent = "64 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Adjust Height"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Adjust height increment."; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "11 37"; + extent = "49 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Set Height"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Elevation for set height operation."; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "11 62"; + extent = "60 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Scale Height"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Scale height increment."; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "10 87"; + extent = "70 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Smooth Factor"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Smoothing factor -- lower values are less agressive."; + maxLength = "255"; + }; + }; + new GuiButtonCtrl() { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "218 205"; + extent = "80 20"; + minExtent = "8 8"; + visible = "1"; + command = "Canvas.popDIalog(TerrainEditorValuesSettingsGui);"; + helpTag = "0"; + text = "OK"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + new GuiControl() { + profile = "ToolsGuiWindowProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "7 27"; + extent = "188 212"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + + new GuiFilterCtrl(TESoftSelectFilter) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 22"; + extent = "155 162"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + controlPoints = "7"; + filter = "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 4"; + extent = "67 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Soft Selection"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "This spline scale modifies the hardness of the brush. Left is center, right is outer edge."; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "12 189"; + extent = "8 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "0"; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "12 26"; + extent = "8 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "1"; + maxLength = "255"; + }; + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "60 190"; + extent = "45 18"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "<Radius>"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Brush radius for Selection Mode."; + maxLength = "255"; + }; + new GuiTextEditCtrl() { + profile = "ToolsGuiTextEditProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "125 187"; + extent = "50 18"; + minExtent = "8 8"; + visible = "1"; + variable = "ETerrainEditor.softSelectRadius"; + command = "ETerrainEditor.softSelectRadius = $ThisControl.getValue();"; + helpTag = "0"; + maxLength = "255"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Brush radius for Selection Mode."; + }; + }; + new GuiButtonCtrl(TESettingsApplyButton) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "307 205"; + extent = "80 20"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + text = "Apply"; + groupNum = "-1"; + buttonType = "PushButton"; + }; + }; +}; +//--- OBJECT WRITE END --- + diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui new file mode 100644 index 000000000..8cdba481d --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui @@ -0,0 +1,637 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(EWTerrainPainterToolbar,EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "TerrainPainterToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "800 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "6 7"; + extent = "70 16"; + minExtent = "8 8"; + visible = "1"; + text = "Brush Settings"; + maxLength = "255"; + helpTag = "0"; + }; + + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "760 40"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiControl(EWTerrainPainterToolbarBrushType){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "83 2"; + Extent = "94 27"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "ellipse"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Circle Brush (V)"; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/circleBrush"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "box"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "31 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Box Brush (B)"; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/boxBrush"; + }; + + /* + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "selection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "62 0"; + Extent = "29 27"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.toggleBrushType($ThisControl);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles the brush type."; + hovertime = "750"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/maskBrush"; + }; + */ + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "152 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(PaintBrushSizeTextEditContainer) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "145 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "21 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Size"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + profile="ToolsGuiNumericDropSliderTextProfile"; + position = "49 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setBrushSize( $ThisControl.getText() );"; + validate = "TerrainPainterPlugin.validateBrushSize();"; + hovertime = "1000"; + text = "9"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(PaintBrushSizeSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the size of the brush (CTRL + Mouse Wheel)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "270 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(PaintBrushSlopeControl) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "262 5"; + Extent = "256 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "21 5"; + Extent = "78 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Allows painting on the terrain within a specified slope"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Slope Mask Min"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "SlopeMinAngle"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "104 2"; + Extent = "51 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + validate = "TerrainPainterPlugin.validateSlopeMinAngle();"; + Command = "ETerrainEditor.setSlopeLimitMinAngle( $ThisControl.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Minimum terrain angle that will be paintable"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "0.0"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "137 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Minimum terrain angle that will be paintable"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + Command = "Canvas.pushDialog(PaintBrushSlopeMinContainer);"; + }; + new GuiTextCtrl() { + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "165 5"; + Extent = "27 10"; + MinExtent = "8 2"; + text = "Max"; + tooltip = "Max terrain angle that will be paintable"; + }; + new GuiTextEditCtrl() { + internalName = "SlopeMaxAngle"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "190 2"; + Extent = "51 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + validate = "TerrainPainterPlugin.validateSlopeMaxAngle();"; + Command = "ETerrainEditor.setSlopeLimitMaxAngle( $ThisControl.getText() );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Max terrain angle that will be paintable"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "90.0"; + maxLength = "4"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "223 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + tooltip = "Max terrain angle that will be paintable"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + Command = "Canvas.pushDialog(PaintBrushSlopeMaxContainer);"; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "525 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(PaintBrushPressureTextEditContainer,EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "540 5"; + Extent = "120 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 5"; + Extent = "47 10"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Pressure"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "textEdit"; + isContainer = "0"; + profile="ToolsGuiNumericDropSliderTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "49 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.setBrushPressure( ($ThisControl.getValue() / 100) );"; + hovertime = "1000"; + text = "100"; + maxLength = "3"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "83 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(PaintBrushPressureSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes the pressure (CTRL + SHIFT + Mouse Wheel)"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- + +function setTerrainEditorMinSlope(%value) +{ + %val = ETerrainEditor.setSlopeLimitMinAngle( %value ); + PaintBrushSlopeControl-->SlopeMinAngle.setValue(mFloatLength( %val, 1 )); +} + +function setTerrainEditorMaxSlope(%value) +{ + %val = ETerrainEditor.setSlopeLimitMaxAngle( %value ); + PaintBrushSlopeControl-->SlopeMaxAngle.setValue(mFloatLength( %val, 1 )); +} + +new GuiMouseEventCtrl(PaintBrushSizeSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(PaintBrushSizeTextEditContainer.position) + firstWord(EWTerrainPainterToolbar.position)+11 SPC + (getWord(PaintBrushSizeTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "PaintBrushSizeTextEditContainer-->textEdit.setValue(mFloatLength( ($ThisControl.getValue()), 2 )); ETerrainEditor.setBrushSize( $ThisControl.value );"; + range = "1 256"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(PaintBrushSlopeMinContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(PaintBrushSlopeControl.position) + firstWord(EWTerrainPainterToolbar.position)+firstWord(PaintBrushSlopeControl->SlopeMinAngle.position) - 40 SPC + (getWord(PaintBrushSlopeControl, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "PaintBrushSlopeControl-->SlopeMinAngle.setValue(mFloatLength( ($ThisControl.getValue()), 1 )); ETerrainEditor.setSlopeLimitMinAngle(mFloatLength( ($ThisControl.getValue()), 1 ));TerrainPainterPlugin.validateSlopeMinAngle();"; + range = "0 89.9"; + ticks = "0"; + value = "0"; + }; +}; + +function PaintBrushSlopeMinContainer::onWake(%this) +{ + %this-->slider.setValue(PaintBrushSlopeControl-->SlopeMinAngle.getText()); +} + +new GuiMouseEventCtrl(PaintBrushSlopeMaxContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(PaintBrushSlopeControl.position) + firstWord(EWTerrainPainterToolbar.position)+firstWord(PaintBrushSlopeControl->SlopeMaxAngle.position) - 40 SPC + (getWord(PaintBrushSlopeControl, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "PaintBrushSlopeControl-->SlopeMaxAngle.setValue(mFloatLength( ($ThisControl.getValue()), 1 )); ETerrainEditor.setSlopeLimitMaxAngle(mFloatLength( ($ThisControl.getValue()), 1 ));TerrainPainterPlugin.validateSlopeMaxAngle();"; + range = "0.1 90.0"; + ticks = "0"; + value = "0"; + }; +}; + +function PaintBrushSlopeMaxContainer::onWake(%this) +{ + %this-->slider.setValue(PaintBrushSlopeControl-->SlopeMaxAngle.getText()); +} + +function PaintBrushSlopeMaxContainer::init(%this) +{ + %this-->slider.setValue("90.0"); +} + +new GuiMouseEventCtrl(PaintBrushPressureSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(PaintBrushPressureTextEditContainer.position) + firstWord(EWTerrainPainterToolbar.position) SPC + (getWord(PaintBrushPressureTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "PaintBrushPressureTextEditContainer-->textEdit.setValue(mFloatLength( ($ThisControl.getValue()), 2 )); ETerrainEditor.setBrushPressure( $ThisControl.value );"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; +}; + +new GuiMouseEventCtrl(PaintBrushSoftnessSliderCtrlContainer,EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(PaintBrushSoftnessTextEditContainer.position) + firstWord(EWTerrainPainterToolbar.position) SPC + (getWord(PaintBrushSoftnessTextEditContainer, 1)) + 25; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "PaintBrushSoftnessTextEditContainer-->textEdit.setValue(mFloatLength( ($ThisControl.getValue()), 2 )); ETerrainEditor.setBrushSoftness( $ThisControl.value );"; + range = "0 1"; + ticks = "0"; + value = "0"; + }; +}; + diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterWindow.ed.gui new file mode 100644 index 000000000..a08c05841 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TerrainPainterWindow.ed.gui @@ -0,0 +1,249 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainPainterContainer,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(EPainter) { + canSaveDynamicFields = "0"; + internalName = "TerrainPainter"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 SPC getWord(EditorGuiToolbar.extent, 1)+249; + Extent = "210 446"; + MinExtent = "210 100"; + canSave = "1"; + isDecoy = "0"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "152 300"; + closeCommand = "EPainter.parentGroup.setVisible(false);"; + EdgeSnap = "1"; + text = "Terrain Painter Material Selector"; + + new GuiScrollCtrl( EPainterScroll ) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 24"; + Extent = "202 418"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "3 1 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiStackControl( EPainterStack ) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theMaterialList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 3"; + Extent = "200 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + new GuiWindowCollapseCtrl(EPainterPreview) { + canSaveDynamicFields = "0"; + internalName = "TerrainPainterPreview"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord($pref::Video::mode, 0) - 209 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "210 251"; + MinExtent = "210 251"; + canSave = "1"; + isDecoy = "0"; + Visible = "0"; + hovertime = "1000"; + Docking = "None"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "152 300"; + closeCommand = "EPainter.parentGroup.setVisible(false);"; + EdgeSnap = "1"; + text = "Terrain Painter Material Preview"; + + new GuiContainer(){ + Docking = "Client"; + Margin = "3 22 3 3"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 24"; + Extent = "202 202"; + + new GuiBitmapCtrl(ETerrainMaterialSelected) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 202"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + wrap = "0"; + bitmap= "tools/materialEditor/gui/unknownImage"; + }; + new GuiBitmapCtrl(ETerrainMaterialSelectedBorder) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 202"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/terrainpainter/terrain-painter-border-large"; + wrap = "0"; + }; + }; + new GuiButtonCtrl(ETerrainMaterialSelectedEdit) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "170 229"; + Extent = "36 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "0"; + Command = "TerrainMaterialDlg.show(ETerrainMaterialSelected.selectedMatIndex, ETerrainMaterialSelected.selectedMat, EPainter_TerrainMaterialUpdateCallback);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl(TerrainTextureText) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "5 230"; + Extent = "162 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "100 229"; + Extent = "50 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "autoLayers();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + tooltip = "Generate a layer mask for this material."; + hovertime = "1000"; + text = "AutoPaint"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TimeAdjustGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TimeAdjustGui.ed.gui new file mode 100644 index 000000000..8eaf11d4c --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TimeAdjustGui.ed.gui @@ -0,0 +1,214 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TimeAdjustGui, EditorGuiGroup) { + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "1"; + + new GuiWindowCtrl() { + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Time Adjust Gui"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "338 63"; + Extent = "462 84"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + closeCommand = "Canvas.popDialog(TimeAdjustGui);"; + + new GuiSliderCtrl(TimeAdjustSliderCtrl) { + range = "0 1"; + ticks = "100"; + value = "0.1"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "37 27"; + Extent = "389 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + altCommand = "$ThisControl.onAction();"; + }; + new GuiTextCtrl() { + text = "Sunrise"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "11 53"; + Extent = "59 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Sunset"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "206 53"; + Extent = "59 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Noon"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "108 53"; + Extent = "59 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Midnight"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "307 53"; + Extent = "59 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Sunrise"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "393 53"; + Extent = "59 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function TimeAdjustSliderCtrl::onAction(%this) +{ + // NOTE: Though this is a GuiControl which exists on the client-side + // we access and modify the server-side TimeOfDay object. This is acceptible + // because this is a "tools" gui which is not intended for a real-game + // or multiplayer situation. + + if ( !isObject( %this.tod ) ) + { + if ( isObject( MissionGroup ) ) + { + for ( %i = 0; %i < MissionGroup.getCount(); %i++ ) + { + %obj = MissionGroup.getObject( %i ); + + if ( %obj.getClassName() $= "TimeOfDay" ) + { + %this.tod = %obj; + break; + } + } + } + } + + if ( !isObject( %this.tod ) ) + return; + + %this.tod.time = %this.getValue(); +} + +function toggleTimeAdjustGui() +{ + if ( TimeAdjustGui.isAwake() ) + Canvas.popDialog( TimeAdjustGui ); + else + Canvas.pushDialog( TimeAdjustGui ); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ConvexEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ConvexEditorPalette.ed.gui new file mode 100644 index 000000000..e32cdfb4a --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ConvexEditorPalette.ed.gui @@ -0,0 +1,102 @@ +%paletteId = new GuiControl(ConvexEditorPalette, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ConvexEditorNoneModeBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Arrow (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + Command = "GlobalGizmoProfile.mode = \"None\";"; + }; + + new GuiBitmapButtonCtrl(ConvexEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "28 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Selection (2)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/translate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + Command = "GlobalGizmoProfile.mode = \"Move\";"; + }; + + new GuiBitmapButtonCtrl(ConvexEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "56 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Selection (3)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/rotate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + Command = "GlobalGizmoProfile.mode = \"Rotate\";"; + }; + + new GuiBitmapButtonCtrl(ConvexEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "84 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Selection (4)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/scale"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + Command = "GlobalGizmoProfile.mode = \"Scale\";"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/DecalEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/DecalEditorPalette.ed.gui new file mode 100644 index 000000000..1e4053c8e --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/DecalEditorPalette.ed.gui @@ -0,0 +1,121 @@ +%paletteId = new GuiControl(DecalEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(EDecalEditorSelectDecalBtn) { + canSaveDynamicFields = "0"; + internalName = "SelectDecalMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "DecalEditorGui.setMode(\"SelectDecalMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Decal (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EDecalEditorMoveDecalBtn) { + canSaveDynamicFields = "0"; + internalName = "MoveDecalMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "DecalEditorGui.setMode(\"MoveDecalMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Decal (2)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/move-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EDecalEditorRotateDecalBtn) { + canSaveDynamicFields = "0"; + internalName = "RotateDecalMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "DecalEditorGui.setMode(\"RotateDecalMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Decal (3)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/rotate-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EDecalEditorScaleDecalBtn) { + canSaveDynamicFields = "0"; + internalName = "ScaleDecalMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "DecalEditorGui.setMode(\"ScaleDecalMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Decal (4)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/scale-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EDecalEditorAddDecalBtn) { + canSaveDynamicFields = "0"; + internalName = "AddDecalMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "DecalEditorGui.setMode(\"AddDecalMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add Decal (5)"; + hovertime = "1000"; + bitmap = "tools/decalEditor/add-decal"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ForestEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ForestEditorPalette.ed.gui new file mode 100644 index 000000000..a1cc96ef5 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ForestEditorPalette.ed.gui @@ -0,0 +1,163 @@ +%paletteId = new GuiControl(ForestEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ForestEditorSelectModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorSelectMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"None\"; ForestEditorGui.setActiveTool(ForestTools->SelectionTool);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Item (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ForestEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorMoveMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"Move\"; ForestEditorGui.setActiveTool(ForestTools->SelectionTool);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Item (2)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/translate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ForestEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorRotateMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"Rotate\"; ForestEditorGui.setActiveTool(ForestTools->SelectionTool);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Item (3)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/rotate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ForestEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorScaleMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"Scale\"; ForestEditorGui.setActiveTool(ForestTools->SelectionTool);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Item (4)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/scale"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ForestEditorPaintModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorPaintMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.setActiveTool( ForestTools->BrushTool ); ForestTools->BrushTool.mode = \"Paint\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Paint (5)"; + hovertime = "1000"; + bitmap = "tools/forestEditor/images/paint-forest-btn"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ForestEditorEraseModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorEraseMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.setActiveTool( ForestTools->BrushTool ); ForestTools->BrushTool.mode = \"Erase\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Erase (6)"; + hovertime = "1000"; + bitmap = "tools/forestEditor/images/erase-all-btn"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ForestEditorEraseSelectedModeBtn) { + canSaveDynamicFields = "0"; + internalName = "ForestEditorEraseSelectedMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ForestEditorGui.setActiveTool( ForestTools->BrushTool ); ForestTools->BrushTool.mode = \"EraseSelected\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Erase Selected (7)"; + hovertime = "1000"; + bitmap = "tools/forestEditor/images/erase-element-btn"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/MeshRoadEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/MeshRoadEditorPalette.ed.gui new file mode 100644 index 000000000..b005ac958 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/MeshRoadEditorPalette.ed.gui @@ -0,0 +1,163 @@ +%paletteId = new GuiControl(MeshRoadEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(EMeshRoadEditorSelectModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorSelectMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.prepSelectionMode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Mesh Road (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EMeshRoadEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorMoveMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorMoveMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Point (2)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/move-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EMeshRoadEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorRotateMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorRotateMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Point (3)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/rotate-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EMeshRoadEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorScaleMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorScaleMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Point (4)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/scale-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EMeshRoadEditorAddModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorAddRoadMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorAddRoadMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Create Road (5)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-mesh-road"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EMeshRoadEditorInsertModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorInsertPointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorInsertPointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Insert Point (+)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(EMeshRoadEditorRemoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "MeshRoadEditorRemovePointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "MeshRoadEditorGui.setMode(\"MeshRoadEditorRemovePointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Remove Point (-)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/subtract-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/NavEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/NavEditorPalette.ed.gui new file mode 100644 index 000000000..506c525b2 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/NavEditorPalette.ed.gui @@ -0,0 +1,130 @@ +%paletteId = new GuiControl(NavEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ENavEditorSelectModeBtn) { + canSaveDynamicFields = "1"; + class = ENavEditorPaletteButton; + internalName = "NavEditorSelectMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "NavEditorGui.prepSelectionMode();"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "View NavMesh (1)."; + DetailedDesc = ""; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/visibility-toggle"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ENavEditorLinkModeBtn) { + canSaveDynamicFields = "1"; + class = ENavEditorPaletteButton; + internalName = "NavEditorLinkMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "NavEditorGui.setMode(\"LinkMode\");"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "Create off-mesh links (2)."; + DetailedDesc = "Click to select/add. Shift-click to add multiple end points."; + hovertime = "1000"; + bitmap = "tools/navEditor/images/nav-link"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ENavEditorCoverModeBtn) { + canSaveDynamicFields = "1"; + class = ENavEditorPaletteButton; + internalName = "NavEditorCoverMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "NavEditorGui.setMode(\"CoverMode\");"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "Edit cover (3)."; + DetailedDesc = ""; + hovertime = "1000"; + bitmap = "tools/navEditor/images/nav-cover"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ENavEditorTileModeBtn) { + canSaveDynamicFields = "1"; + class = ENavEditorPaletteButton; + internalName = "NavEditorTileMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "NavEditorGui.setMode(\"TileMode\");"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "View tiles (4)."; + DetailedDesc = "Click to select."; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/select-bounds"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ENavEditorTestModeBtn) { + canSaveDynamicFields = "1"; + class = ENavEditorPaletteButton; + internalName = "NavEditorTestMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "NavEditorGui.setMode(\"TestMode\");"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "Test pathfinding (5)."; + DetailedDesc = "Click to select/move character, CTRL-click to spawn, SHIFT-click to deselect."; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/toolbar/3rd-person-camera"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RiverEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RiverEditorPalette.ed.gui new file mode 100644 index 000000000..5fea48b1b --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RiverEditorPalette.ed.gui @@ -0,0 +1,163 @@ +%paletteId = new GuiControl(RiverEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ERiverEditorSelectModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorSelectMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.prepSelectionMode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select River (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERiverEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorMoveMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorMoveMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Point (2)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/move-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERiverEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorRotateMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorRotateMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Point (3)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/rotate-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERiverEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorScaleMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorScaleMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Point (4)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/scale-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERiverEditorAddModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorAddRiverMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorAddRiverMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Create River (5)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-river"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERiverEditorInsertModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorInsertPointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorInsertPointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Insert Point (+)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ERiverEditorRemoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RiverEditorRemovePointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RiverEditorGui.setMode(\"RiverEditorRemovePointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Remove Point (-)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/subtract-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RoadEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RoadEditorPalette.ed.gui new file mode 100644 index 000000000..712ea774f --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/RoadEditorPalette.ed.gui @@ -0,0 +1,144 @@ +%paletteId = new GuiControl(RoadEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ERoadEditorSelectModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorSelectMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.prepSelectionMode();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Road (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ERoadEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorMoveMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.setMode(\"RoadEditorMoveMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Point (2)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/move-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ERoadEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorScaleMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.setMode(\"RoadEditorScaleMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Point (4)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/scale-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERoadEditorAddModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorAddRoadMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.setMode(\"RoadEditorAddRoadMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Create Road (5)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-road-path"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl(ERoadEditorInsertModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorInsertPointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.setMode(\"RoadEditorInsertPointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Insert Point (+)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/add-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ERoadEditorRemoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "RoadEditorRemovePointMode"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "RoadEditorGui.setMode(\"RoadEditorRemovePointMode\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Remove Point (-)"; + hovertime = "1000"; + bitmap = "tools/worldEditor/images/road-river/subtract-point"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ShapeEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ShapeEditorPalette.ed.gui new file mode 100644 index 000000000..e8dfaf0e9 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/ShapeEditorPalette.ed.gui @@ -0,0 +1,102 @@ +%paletteId = new GuiControl(ShapeEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(ShapeEditorNoneModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorSelectArrow"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"None\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Arrow (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ShapeEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorMove"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "28 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"Move\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Selection (2)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/translate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ShapeEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorRotate"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "56 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.mode = \"Rotate\";"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Selection (3)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/rotate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(ShapeEditorSunModeBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "84 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = "ShapeEdShapeView.editSun"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate sun"; + hovertime = "1000"; + bitmap = "tools/shapeEditor/images/sun-btn"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainEditPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainEditPalette.ed.gui new file mode 100644 index 000000000..8f776c932 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainEditPalette.ed.gui @@ -0,0 +1,235 @@ +%paletteId = new GuiControl(TerrainEditorPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "brushAdjustHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( brushAdjustHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Grab Terrain (1)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/brushAdjustHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "raiseHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( raiseHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Raise Height (2)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/raiseHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "lowerHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "36 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( lowerHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Lower Height (3)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/lowerHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "smoothHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "144 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( smoothHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Smooth (4)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/smoothHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "smoothSlope"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "144 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( smoothSlope );"; + tooltipprofile = "GuiToolTipProfile"; + ToolTip = "Smooth Slope (5)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/softCurve"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "paintNoise"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "72 36"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( paintNoise );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Paint Noise (6)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/brushPaintNoise"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "flattenHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "108 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( flattenHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Flatten (7)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/flattenHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "setHeight"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "180 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( setHeight );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Set Height (8)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/setHeight"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "setEmpty"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 36"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( setEmpty );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Clear Terrain (9)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/setEmpty"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "clearEmpty"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "36 36"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "ETerrainEditor.switchAction( clearEmpty );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Restore Terrain (0)"; + hovertime = "750"; + text = "Button"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + bitmap = "tools/worldEditor/images/clearEmpty"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainPainterPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainPainterPalette.ed.gui new file mode 100644 index 000000000..091f7a1c0 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/TerrainPainterPalette.ed.gui @@ -0,0 +1,14 @@ +%paletteId = new GuiControl(TerrainPainterPalette,EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/WorldEditorPalette.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/WorldEditorPalette.ed.gui new file mode 100644 index 000000000..421b52fe4 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/WorldEditorPalette.ed.gui @@ -0,0 +1,98 @@ +%paletteId = new GuiControl(WorldEditorInspectorPalette, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(EWorldEditorNoneModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorSelectArrow"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Select Arrow (1)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/arrow"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EWorldEditorMoveModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorMove"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "28 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Move Selection (2)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/translate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EWorldEditorRotateModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorRotate"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "56 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Rotate Selection (3)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/rotate"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EWorldEditorScaleModeBtn) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorScale"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "84 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Scale Selection (4)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/scale"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/init.cs b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/init.cs new file mode 100644 index 000000000..637739745 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteGroups/init.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EWToolsPaletteWindow::loadToolsPalettes() +{ + %filespec = "tools/worldEditor/gui/ToolsPaletteGroups/*.ed.gui"; + + // were executing each gui file and adding them to the ToolsPaletteArray + for( %file = findFirstFile(%filespec); %file !$= ""; %file = findNextFile(%filespec)) + { + exec( %file ); + %paletteGroup = 0; + + %i = %paletteId.getCount(); + for( ; %i != 0; %i--) + { + %paletteId.getObject(0).visible = 0; + %paletteId.getObject(0).groupNum = %paletteGroup; + %paletteId.getObject(0).paletteName = %paletteId.getName(); + ToolsPaletteArray.addGuiControl(%paletteId.getObject(0)); + } + %paletteGroup++; + } + + %filespec = "tools/worldEditor/gui/ToolsPaletteGroups/*.ed.gui.edso"; + + // were executing each gui file and adding them to the ToolsPaletteArray + for( %file = findFirstFile(%filespec); %file !$= ""; %file = findNextFile(%filespec)) + { + exec( %file ); + %paletteGroup = 0; + + %i = %paletteId.getCount(); + for( ; %i != 0; %i--) + { + %paletteId.getObject(0).visible = 0; + %paletteId.getObject(0).groupNum = %paletteGroup; + %paletteId.getObject(0).paletteName = %paletteId.getName(); + ToolsPaletteArray.addGuiControl(%paletteId.getObject(0)); + } + %paletteGroup++; + } +} + +function EWToolsPaletteWindow::init() +{ + EWToolsPaletteWindow.loadToolsPalettes(); +} + +function EWToolsPaletteWindow::togglePalette(%this, %paletteName) +{ + // since the palette window ctrl auto adjusts to child ctrls being visible, + // loop through the array and pick out the children that belong to a certain tool + // and label them visible or not visible + + for( %i = 0; %i < ToolsPaletteArray.getCount(); %i++ ) + ToolsPaletteArray.getObject(%i).visible = 0; + + %windowMultiplier = 0; + %paletteNameWordCount = getWordCount( %paletteName ); + for(%pallateNum = 0; %pallateNum < %paletteNameWordCount; %pallateNum++) + { + %currentPalette = getWord(%paletteName, %pallateNum); + for( %i = 0; %i < ToolsPaletteArray.getCount(); %i++ ) + { + if( ToolsPaletteArray.getObject(%i).paletteName $= %currentPalette) + { + ToolsPaletteArray.getObject(%i).visible = 1; + %windowMultiplier++; + } + } + } + + // auto adjust the palette window extent according to how many + // children controls we found; if none found, the palette window becomes invisible + if( %windowMultiplier == 0 || %paletteName $= "") + EWToolsPaletteWindow.visible = 0; + else + { + EWToolsPaletteWindow.visible = 1; + EWToolsPaletteWindow.extent = getWord(EWToolsPaletteWindow.extent, 0) SPC (16 + 26 * %windowMultiplier); + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui new file mode 100644 index 000000000..b6b9c5507 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsPaletteWindow.ed.gui @@ -0,0 +1,68 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(EWToolsPaletteWindow) { + canSaveDynamicFields = "0"; + internalName = "ToolsPaletteWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiToolbarWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Extent = "36 24"; + MinExtent = "36 24"; + Position = "-1 63"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + EdgeSnap = false; + text = ""; + class = "EWToolsPaletteWindowClass"; + + new GuiDynamicCtrlArrayControl(ToolsPaletteArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "5 15"; + Extent = "35 514"; + MinExtent = "35 514"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + colCount = "1"; + colSize = "26"; + RowSize = "22"; + rowSpacing = "3"; + colSpacing = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui new file mode 100644 index 000000000..f57c39be7 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/ToolsToolbar.ed.gui @@ -0,0 +1,72 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiContainer(EWToolsToolbar) { + canSaveDynamicFields = "0"; + Enabled = "0"; + internalName = "ToolsToolbar"; + isContainer = "1"; + Profile = "editorMenubarProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 31"; + Extent = "0 33"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + isClosed = "0"; + isDynamic = "0"; + + new GuiDynamicCtrlArrayControl(ToolsToolbarArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "4 3"; + Extent = "264 32"; + MinExtent = "1024 32"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + colCount = "1"; + colSize = "29"; + RowSize = "27"; + rowSpacing = "2"; + colSpacing = "4"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + internalName = "resizeArrow"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = getWord(EWToolsToolbar.Extent, 0) - 7 SPC "0"; + extent = "7 33"; + MinExtent = "7 2"; + canSave = "1"; + Visible = "1"; + Command = "EWToolsToolbar.ToggleSize();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Collapse Toolbar"; + hovertime = "750"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/collapse-toolbar"; + }; + new GuiDecoyCtrl(EWToolsToolbarDecoy) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "1 1"; + extent = "35 31"; + minExtent = "8 8"; + visible = "0"; + helpTag = "0"; + useMouseEvents = "1"; + isDecoy = "1"; + }; +}; + diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/TransformSelectionWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/TransformSelectionWindow.ed.gui new file mode 100644 index 000000000..26324ea74 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/TransformSelectionWindow.ed.gui @@ -0,0 +1,1026 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TransformSelectionContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(ETransformSelection) { + internalName = "TransformSelectionWindow"; + Enabled = "1"; + isContainer = "1"; + profile = "ToolsGuiWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + resizeWidth = "1"; + resizeHeight = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + position = "40 70"; + extent = "180 508"; + MinExtent = "120 130"; + text = "Transform Selection"; + closeCommand = "ETransformSelection.hideDialog();"; + EdgeSnap = "0"; + canCollapse = "0"; + visible = "0"; + Margin = "5 5 5 5"; + Padding = "5 5 5 5"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "5 5 5 4"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 2"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "5"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "140 300"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "140 106"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "DoPosition"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "1 0"; + Extent = "190 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Apply changes to position"; + text = "Position"; + Command = ""; + }; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "GetPosButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "100 0"; + Extent = "30 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.getAbsPosition();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Get absolute position for selected objects"; + text = "Get"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 22"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "X:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "PosX"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 22"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 44"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Y:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "PosY"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 44"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 66"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Z:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "PosZ"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 66"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "PosRelative"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 88"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Add values to current position (checked) or set absolute position (unchecked)"; + text = "Relative"; + Command = ""; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 0"; + Extent = "100 2"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-v.png"; + }; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "140 128"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "DoRotation"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "1 0"; + Extent = "190 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Apply changes to rotation"; + text = "Rotation"; + Command = ""; + }; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "GetRotButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "100 0"; + Extent = "30 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.getAbsRotation();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Get absolute rotation for selected objects"; + text = "Get"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 22"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "H:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "Heading"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 22"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 44"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "P:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "Pitch"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 44"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 66"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "B:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "Bank"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 66"; + Extent = "90 18"; + text ="0.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "RotRelative"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 88"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Add values to current rotation (checked) or set absolute rotation (unchecked)"; + text = "Relative"; + Command = "ETransformSelection.RotRelativeChanged();"; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "RotLocal"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 110"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Use object's local origin to rotate from"; + text = "Local Center"; + Command = "ETransformSelection.RotLocalChanged();"; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "0 0"; + Extent = "100 2"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-v.png"; + }; + + new GuiTabBookCtrl() { + internalName = "ScaleTabBook"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "140 176"; + MinExtent = "16 16"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "3 2 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "0"; + MinTabWidth = "50"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "140 156"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Scale"; + maxLength = "1024"; + + new GuiBitmapBorderCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "134 156"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + hovertime = "1000"; + }; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "134 156"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "DoScale"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "2 4"; + Extent = "100 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Apply changes to scale"; + text = "Scale"; + Command = ""; + }; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "GetScaleButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "100 4"; + Extent = "30 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.getAbsScale();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Get absolute scale for selected objects"; + text = "Get"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 26"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "X:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "ScaleX"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 26"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 48"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Y:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "ScaleY"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 48"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 70"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Z:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "ScaleZ"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 70"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "ScaleRelative"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 92"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Add values to current scale (checked) or set absolute scale (unchecked)"; + text = "Relative"; + Command = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "ScaleLocal"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 114"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Use object's local origin to scale from"; + text = "Local Center"; + Command = ""; + }; + + new GuiCheckBoxCtrl(ETransformSelectionScaleProportional){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "ScaleProportional"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 136"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Scale equally in all directions"; + text = "Constrain Proportions"; + Command = ""; + }; + }; + }; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "140 156"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Size"; + maxLength = "1024"; + + new GuiBitmapBorderCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "134 156"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + hovertime = "1000"; + }; + + new GuiControl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "134 156"; + MinExtent = "16 16"; + Visible = "1"; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "DoSize"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "2 4"; + Extent = "100 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Apply changes to size"; + text = "Size"; + Command = ""; + }; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "GetSizeButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "100 4"; + Extent = "30 18"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.getAbsSize();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Get absolute size for selected objects"; + text = "Get"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 26"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "X:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "SizeX"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 26"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 48"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Y:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "SizeY"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 48"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "20 70"; + Extent = "20 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Z:"; + maxLength = "1024"; + }; + + new GuiTextEditCtrl() { + class = "ETransformSelectionTextEdit"; + internalName = "SizeZ"; + profile="ToolsGuiNumericTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "40 70"; + Extent = "90 18"; + text ="1.0"; + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "SizeRelative"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 92"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Add values to current size (checked) or set absolute size (unchecked)"; + text = "Relative"; + Command = ""; + }; + + new GuiCheckBoxCtrl(){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "SizeLocal"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 114"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Use object's local origin to size from"; + text = "Local Center"; + Command = ""; + }; + + new GuiCheckBoxCtrl(ETransformSelectionSizeProportional){ + class = "ETransformSelectionCheckBoxClass"; + internalName = "SizeProportional"; + Enabled = "1"; + Profile = "ToolsGuiCheckBoxProfile"; + position = "40 136"; + Extent = "120 18"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Size equally in all directions"; + text = "Constrain Proportions"; + Command = ""; + }; + }; + }; + }; + }; + }; + + new GuiContainer(){ + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "190 24"; + Docking = "Bottom"; + Margin = "5 5 5 5"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "ApplyButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "50 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.apply();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Apply"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiButtonCtrl() { + class = "ETransformSelectionButtonClass"; + internalName = "CloseButton"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "140 0"; + Extent = "50 23"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "ETransformSelection.hideDialog();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Close"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/VisibilityLayerWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/VisibilityLayerWindow.ed.gui new file mode 100644 index 000000000..3dbed5244 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/VisibilityLayerWindow.ed.gui @@ -0,0 +1,278 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(VisibilityLayerContainer, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiModelessDialogProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl(EVisibility) { + internalName = "VisibilityLayerWindow"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiToolbarWindowProfile"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + Position = getWord(visibilityToggleBtn.position, 0) SPC getWord(EditorGuiToolbar.extent, 1); + Extent = "161 250"; //175 696 = full length + MinExtent = "161 86"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + hovertime = "1000"; + Docking = "None"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "152 300"; + closeCommand = ""; + EdgeSnap = "1"; + text = ""; + + new GuiTabBookCtrl(EVisibilityTabBook) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "3 1 3 3"; + Position = "5 24"; + Extent = "170 226"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + TabPosition = "Top"; + TabHeight = "22"; + TabMargin = "7"; + MinTabWidth = "8"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Visual"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theVisOptionsList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Visible"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theClassVisList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Margin = "-1 0 0 0"; + Position = "0 14"; + Extent = "164 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Select"; + maxLength = "255"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Docking = "Client"; + Position = "4 12"; + Extent = "156 190"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "2 0"; + + new GuiStackControl() { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "-2"; + canSaveDynamicFields = "0"; + internalName = "theClassSelList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "1 0"; + Extent = "156 16"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + }; + }; + }; + }; + }; +}; \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorInspectorWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorInspectorWindow.ed.gui new file mode 100644 index 000000000..2d429529d --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorInspectorWindow.ed.gui @@ -0,0 +1,143 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(EWInspectorWindow) { + canSaveDynamicFields = "0"; + internalName = "InspectorWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + Position = getWord($pref::Video::mode, 0) - 209 SPC + getWord(EditorGuiToolbar.extent, 1) + getWord(EWTreeWindow.extent, 1) - 2; + Extent = "210 373"; + MinExtent = "210 140"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "8 8 8 8"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + closeCommand = "EWInspectorWindow.setVisible(false);"; + minSize = "50 50"; + EdgeSnap = "1"; + text = "Inspector"; + + new GuiContainer() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "4 24"; + Extent = "202 304"; + MinExtent = "64 64"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "client"; + Margin = "3 41 3 3"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiEditorScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "202 304"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(Inspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "GuiInspectorProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "202 309"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + dividerMargin = "5"; + superClass = "EditorInspectorBase"; + }; + }; + }; + new GuiMLTextCtrl(FieldInfoControl) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorFieldInfoMLTextProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 328"; + Extent = "205 14"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + lineSpacing = "2"; + allowColorChars = "0"; + maxChars = "-1"; + useURLMouseCursor = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorToolbar.ed.gui new file mode 100644 index 000000000..79f34211d --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorToolbar.ed.gui @@ -0,0 +1,673 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(EWorldEditorToolbar, EditorGuiGroup) { + canSaveDynamicFields = "0"; + internalName = "WorldEditorToolbar"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "306 0"; + Extent = "550" SPC getWord(EditorGuiToolbar.extent, 1); + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + + new GuiStackControl() { + StackingType = "Horizontal"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "0"; + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "0 3"; + Extent = "190 31"; + MinExtent = "16 16"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + changeChildSizeToFit = false; + padding = "2"; + + new GuiBitmapButtonCtrl(FitToSelectionBtn) { + canSaveDynamicFields = "0"; + internalName = ""; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "2 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "commandToServer('EditorCameraAutoFit', EWorldEditor.getSelectionRadius()+1);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Fit View To Selection (F)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/fit-selection"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "34 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiTextCtrl() { + profile = "ToolsGuiTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "37 7"; + extent = "77 16"; + minExtent = "8 8"; + visible = "1"; + text = " World Settings"; + maxLength = "255"; + helpTag = "0"; + }; + + new GuiControl(SnapToBar){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "116 3"; + Extent = "123 27"; + Padding = "4"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "snappingSettingsBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command ="ESnapOptions.ToggleVisibility();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Snapping Options"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + buttonMargin = "0 0"; + bitmap = "tools/gui/images/menubar/snapping-settings"; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = "23 21"; + Extent = "4 4"; + MinExtent = "4 4"; + bitmap = "tools/gui/images/dropdown-button-arrow"; + }; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectGridSnapBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "31 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleSnappingOptions(\"grid\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles grid snapping (G)"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "toggleButton"; + useMouseEvents = "0"; + groupNum = "-1"; + bitmap = "tools/gui/images/menubar/snap-grid"; + textMargin = "4"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectSnapDownBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "62 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleSnappingOptions(\"terrain\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "All objects will snap to the terrain (T)"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "toggleButton"; + useMouseEvents = "0"; + groupNum = "-1"; + bitmap = "tools/gui/images/menubar/snap-terrain"; + textMargin = "4"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectSnapBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "93 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "toggleSnappingOptions(\"soft\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Soft object snapping to other objects (B)"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "toggleButton"; + useMouseEvents = "0"; + groupNum = "-1"; + bitmap = "tools/gui/images/menubar/snap-objects"; + textMargin = "4"; + }; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + internalName = "softSnapSizeTextEditContainer"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "178 5"; + Extent = "56 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "softSnapSizeTextEdit"; + isContainer = "0"; + HorizSizing = "right"; + profile="ToolsGuiNumericDropSliderTextProfile"; + VertSizing = "bottom"; + position = "0 2"; + Extent = "42 16"; + MinExtent = "8 16"; + canSave = "1"; + Visible = "1"; + AltCommand = "EWorldEditor.setSoftSnapSize( $ThisControl.getText() ); EWorldEditor.syncGui();"; + tooltip = "Object Snapping Distance"; + hovertime = "1000"; + text = "9"; + maxLength = "6"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "34 2"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "Canvas.pushDialog(softSnapSizeSliderCtrlContainer);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Changes size of the soft snap region"; + hovertime = "750"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/dropslider"; + }; + }; + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "269 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "boundingBoxColBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "274 3"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Variable = "EWorldEditor.boundingBoxCollision"; + Command = "EWorldEditor.boundingBoxCollision = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Object bounds selection toggle (V)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/select-bounds"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "307 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(ToggleButtonBar){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "313 3"; + Extent = "65 27"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "centerObject"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "objectCenterDropdown.toggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles object center (O) and bounds center (P)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/object-center"; + text = "Button"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = "23 21"; + Extent = "4 4"; + MinExtent = "4 4"; + bitmap = "tools/gui/images/dropdown-button-arrow"; + }; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectTransform"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "31 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "objectTransformDropdown.toggle();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Toggles object transform (K) and world transform (L)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/world-transform"; + groupNum = "-1"; + buttonType = "ToggleButton"; + text = ""; + + new GuiBitmapCtrl(){ + HorizSizing = "left"; + VertSizing = "top"; + Position = "23 21"; + Extent = "4 4"; + MinExtent = "4 4"; + bitmap = "tools/gui/images/dropdown-button-arrow"; + }; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "379 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(ToggleNodeBar){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "386 3"; + Extent = "63 27"; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "renderHandleBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Variable = "EWorldEditor.renderObjHandle"; + Command = "EWorldEditor.renderObjHandle = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Enables Render of Object Node Icons (N)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/object-node-icon"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "renderTextBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "33 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Variable = "EWorldEditor.renderObjText"; + Command = "EWorldEditor.renderObjText = $ThisControl.getValue();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Enables Render of Object Node Lables (SHIFT N)"; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/object-node-lable"; + text = ""; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + }; + }; + + new GuiBitmapCtrl() { + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + position = "379 3"; + Extent = "2 26"; + MinExtent = "1 1"; + bitmap = "tools/gui/images/separator-h.png"; + }; + + new GuiControl(PrefabBar){ + isContainer = "1"; + profile = "ToolsGuiDefaultProfile"; + Position = "386 3"; + Extent = "63 27"; + visible = true; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "makePrefabBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Variable = ""; + Command = "EditorMakePrefab();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Make the Selection a Prefab."; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/selection-to-prefab"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "explodePrefabBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "33 0"; + Extent = "29 27"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Variable = ""; + Command = "EditorExplodePrefab();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Explode the Selected Prefab."; + hovertime = "1000"; + bitmap = "tools/gui/images/menubar/explode-prefab"; + text = ""; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + }; + + new GuiContainer(objectCenterDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(EWorldEditorToolbar.position, 0)+getWord(ToggleButtonBar.Position, 0)+getWord(EWorldEditorToolbar-->centerObject.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "132 62"; + isContainer = "1"; + visible = "0"; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectBoxBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "122 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.objectsUseBoxCenter = 0; EWorldEditor.syncGui(); objectCenterDropdown.toggle(); "; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Use object defined center (O)"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/object-center_n"; + text = "Object Center"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectBoundsBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 33 "; + Extent = "122 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.objectsUseBoxCenter = 1; EWorldEditor.syncGui(); objectCenterDropdown.toggle(); "; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Use bounding box center (P)"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/bounds-center_n"; + text = "Bounds Center"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiDecoyCtrl(objectCenterDropdownDecoy) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "132 62"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + useMouseEvents = "1"; + isDecoy = "1"; + }; + }; + + new GuiContainer(objectTransformDropdown){ + Profile = "IconDropdownProfile"; + Position = getWord(EWorldEditorToolbar.position, 0)+getWord(ToggleButtonBar.position, 0)+getWord(EWorldEditorToolbar-->objectTransform.position, 0)-5 SPC getWord(EditorGuiToolbar.extent, 1)-1; + Extent = "147 62"; + isContainer = "1"; + visible ="0"; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "worldTransformBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiIconProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 5"; + Extent = "137 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.setFieldValue(alignment, World); EWorldEditor.syncGui(); objectTransformDropdown.toggle(); "; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Use world normal for transformations (L)"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/world-transform_n"; + text = "World Transform"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiIconButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "objectTransformBtn"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiIconButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 33"; + Extent = "137 25"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "GlobalGizmoProfile.setFieldValue(alignment, Object); EWorldEditor.syncGui(); objectTransformDropdown.toggle(); "; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Use object normal for transformations (K)"; + hovertime = "1000"; + iconBitmap = "tools/gui/images/menubar/object-transform_n"; + text = "Object Transform"; + buttonMargin = "0 4"; + textMargin = "38"; + groupNum = "0"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + }; + + new GuiDecoyCtrl(objectTransformDropdownDecoy) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "147 62"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + useMouseEvents = "1"; + isDecoy = "1"; + }; + }; + }; +}; + +new GuiMouseEventCtrl(softSnapSizeSliderCtrlContainer, EditorGuiGroup) { + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "1024 768"; + minExtent = "8 8"; + visible = "1"; + helpTag = "0"; + class = "EditorDropdownSliderContainer"; + + new GuiSliderCtrl() { + canSaveDynamicFields = "0"; + internalName = "slider"; + isContainer = "0"; + Profile = "ToolsGuiSliderBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = firstWord(EWorldEditorToolbar-->softSnapSizeTextEdit.getGlobalPosition()) - 12 SPC + (getWord(EWorldEditorToolbar-->softSnapSizeTextEdit.getGlobalPosition(), 1)) + 18; + Extent = "112 20"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "softSnapSizeSliderCtrlContainer.onSliderChanged();"; + range = "0.01 10"; + ticks = "0"; + value = "0"; + }; +}; diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui new file mode 100644 index 000000000..9f8b496e0 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/WorldEditorTreeWindow.ed.gui @@ -0,0 +1,577 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCollapseCtrl(EWTreeWindow) { + canSaveDynamicFields = "0"; + internalName = "TreeWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + Position = firstWord($pref::Video::mode) - 209 + SPC getWord(EditorGuiToolbar.extent, 1) -1; + Extent = "210 324"; + MinExtent = "210 140"; + HorizSizing = "windowRelative"; + VertSizing = "windowRelative"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "8 8 8 8"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EWTreeWindow.setVisible(false);"; + EdgeSnap = "1"; + text = "Scene Tree"; + + new GuiTabBookCtrl(EditorTreeTabBook) { + canSaveDynamicFields = "0"; + isContainer = "1"; + internalName = "EditorTree"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "6 27"; + Extent = "197 289"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "3 2 3 3"; + Docking = "client"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "0"; + MinTabWidth = "64"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "197 271"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Scene"; + maxLength = "1024"; + + new GuiTextEditCtrl( EditorTreeFilter ) { + position = "2 4"; + extent = "175 18"; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + class = "GuiTreeViewFilterText"; + treeView = EditorTree; + }; + + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/clear-icon"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "180 5"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + class = "GuiTreeViewFilterClearButton"; + textCtrl = EditorTreeFilter; + }; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 25"; + Extent = "197 246"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTreeViewCtrl(EditorTree) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "1 1"; + Extent = "193 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + showRoot = "1"; + useInspectorTooltips = "1"; + tooltipOnWidthOnly = "1"; + showObjectIds = "0"; + showClassNames = "0"; + showObjectNames = "1"; + showInternalNames = "1"; + showClassNameForUnnamedObjects = "1"; + }; + }; + }; + new GuiTabPageCtrl(EWCreatorWindow) { + canSaveDynamicFields = "0"; + internalName = "CreatorWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiEditorTabPage"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 19"; + Extent = "208 274"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Library"; + maxLength = "1024"; + + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "GuiInspectorProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "198 271"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + }; + new GuiBitmapBorderCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "198 271"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + hovertime = "1000"; + }; + new GuiBitmapBorderCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTabBorderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 25"; + Extent = "198 246"; + MinExtent = "8 2"; + canSave = "0"; + Visible = "1"; + hovertime = "1000"; + }; + new GuiContainer() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "-4 47"; + Extent = "206 228"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "4 4 4 4"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "EditorLightScrollProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 4"; + Extent = "198 220"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOff"; + lockHorizScroll = "false"; + lockVertScroll = "true"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiDynamicCtrlArrayControl(CreatorIconArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "1 1"; + Extent = "197 218"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "20"; + colSize = "64"; + rowCount = "0"; + RowSize = "32"; + rowSpacing = "4"; + colSpacing = "4"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "0"; + dynamicSize = "1"; + }; + }; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "-3 2"; + Extent = "202 55"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(CreatorNavUpButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 28"; + Extent = "20 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EWCreatorWindow.navigateUp();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "0"; + buttonType = "PushButton"; + + Bitmap = "tools/gui/images/folderUp"; + autoSize = "0"; + }; + new GuiPopUpMenuCtrl(CreatorPopupMenu) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "32 28"; + Extent = "163 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Objects"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiTabBookCtrl(CreatorTabBook) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "6 4"; + Extent = "198 21"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "4"; + MinTabWidth = "49"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Scripted"; + maxLength = "1024"; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Meshes"; + maxLength = "1024"; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Level"; + maxLength = "1024"; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Prefabs"; + maxLength = "1024"; + }; + }; + }; + }; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "LockSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "157 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.lockSelection(true); EditorTree.toggleLock();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Lock Selection"; + hovertime = "1000"; + bitmap = "tools/gui/images/lock"; + buttonType = "ToggleButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + + new GuiBitmapButtonCtrl(EWAddSimGroupButton) { + canSaveDynamicFields = "0"; + internalName = "AddSimGroup"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "173 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Add Sim Group"; + hovertime = "1000"; + bitmap = "tools/gui/images/add-simgroup-btn"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + useModifiers = "1"; + }; + + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = "DeleteSelection"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "189 26"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EditorMenuEditDelete();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete Selection"; + hovertime = "1000"; + bitmap = "tools/gui/images/delete"; + buttonType = "PushButton"; + groupNum = "-1"; + text = ""; + useMouseEvents = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiCreateNewTerrainGui.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiCreateNewTerrainGui.gui new file mode 100644 index 000000000..23d65e24a --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiCreateNewTerrainGui.gui @@ -0,0 +1,352 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(CreateNewTerrainGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "640 480"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "182 94"; + Extent = "250 140"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "1"; + AnchorLeft = "1"; + AnchorRight = "1"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "Canvas.popDialog( CreateNewTerrainGui );"; + EdgeSnap = "0"; + text = "Create New Terrain"; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "theName"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "68 30"; + Extent = "171 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + text = "myNewTerrain"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "32 31"; + Extent = "31 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Name:"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 108"; + Extent = "138 24"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "CreateNewTerrainGui.create();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Create New"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + accelerator = "return"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "159 108"; + Extent = "80 24"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "Canvas.popDialog( CreateNewTerrainGui );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + accelerator = "escape"; + }; + new GuiRadioCtrl() { + canSaveDynamicFields = "0"; + internalName = "flatRadio"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiRadioProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "155 80"; + Extent = "40 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Flat"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiRadioCtrl() { + canSaveDynamicFields = "0"; + internalName = "noiseRadio"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiRadioProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "195 80"; + Extent = "45 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Noise"; + groupNum = "1"; + buttonType = "RadioButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "23 56"; + Extent = "40 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Material: "; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "10 81"; + Extent = "52 16"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Resolution:"; + maxLength = "1024"; + }; + new GuiPopUpMenuCtrl() { + canSaveDynamicFields = "0"; + internalName = "theRezList"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "68 80"; + Extent = "57 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiPopUpMenuCtrl() { + canSaveDynamicFields = "0"; + internalName = "theMaterialList"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "68 55"; + Extent = "171 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + }; +}; +//--- OBJECT WRITE END --- +function CreateNewTerrainGui::onWake( %this ) +{ + %this-->theName.setText( "" ); + + %matList = %this-->theMaterialList; + %matList.clear(); + %count = TerrainMaterialSet.getCount(); + for ( %i=0; %i < %count; %i++ ) + %matList.add( TerrainMaterialSet.getObject( %i ).internalName, %i ); + %matList.setSelected( 0 ); + + %rezList = %this-->theRezList; + %rezList.clear(); + %rezList.add( "256", 256 ); + %rezList.add( "512", 512 ); + %rezList.add( "1024", 1024 ); + %rezList.add( "2048", 2048 ); + //%rezList.add( "4096", 4096 ); + %rezList.setSelected( 256 ); + + %this-->flatRadio.setStateOn( true ); +} + +function CreateNewTerrainGui::create( %this ) +{ + %terrainName = %this-->theName.getText(); + %resolution = %this-->theRezList.getSelected(); + %materialName = %this-->theMaterialList.getText(); + %genNoise = %this-->noiseRadio.getValue(); + + %obj = TerrainBlock::createNew( %terrainName, %resolution, %materialName, %genNoise ); + + if( %genNoise ) + ETerrainEditor.isDirty = true; + + if( isObject( %obj ) ) + { + // Submit an undo action. + MECreateUndoAction::submit(%obj); + + assert( isObject( EWorldEditor ), + "ObjectBuilderGui::processNewObject - EWorldEditor is missing!" ); + + // Select it in the editor. + EWorldEditor.clearSelection(); + EWorldEditor.selectObject(%obj); + + // When we drop the selection don't store undo + // state for it... the creation deals with it. + EWorldEditor.dropSelection( true ); + } + + Canvas.popDialog( %this ); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainEditorToolbar.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainEditorToolbar.ed.gui new file mode 100644 index 000000000..e69de29bb diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainExportGui.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainExportGui.gui new file mode 100644 index 000000000..772d4d129 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainExportGui.gui @@ -0,0 +1,310 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainExportGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Profile = "ToolsGuiOverlayProfile"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "1024 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(TerrainExportWindow) { + profile = "ToolsGuiWindowProfile"; + canSaveDynamicFields = "0"; + internalName = "TerrainExport"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "248 248"; + Extent = "290 235"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "1"; + AnchorLeft = "1"; + AnchorRight = "1"; + resizeWidth = "0"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "TerrainExportGui.close();"; + EdgeSnap = "0"; + canCollapse = "0"; + text = "Export Terrain"; + + new GuiScrollCtrl(TerrainSelectScroll) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 43"; + Extent = "272 112"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiListBoxCtrl(TerrainSelectListBox) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "2 2"; + Extent = "248 104"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + AllowMultipleSelections = "1"; + fitParentWidth = "1"; + }; + }; + new GuiTextCtrl(TerrainSelectText) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 25"; + Extent = "88 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Select Terrain(s):"; + maxLength = "1024"; + }; + new GuiTextEditCtrl(SelectFolderTextEdit) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 176"; + Extent = "195 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiButtonCtrl(SelectFolderButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "212 174"; + Extent = "69 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainExportGui.selectFolder();"; + hovertime = "1000"; + text = "Browse"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "174 202"; + Extent = "107 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainExportGui.close();"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl(ExportButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 202"; + Extent = "107 24"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainExportGui.export();"; + hovertime = "1000"; + text = "Export"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl(FolderText) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "9 159"; + Extent = "96 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Folder:"; + maxLength = "1024"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function TerrainExportGui::findAllTerrains( %this ) +{ + TerrainSelectListBox.clearItems(); + + if ( isObject( MegaTerrain ) ) + TerrainSelectListBox.addItem( "MegaTerrain" ); + + // Find all of the terrain files + initContainerTypeSearch( $TypeMasks::TerrainObjectType ); + + while ( (%terrainObject = containerSearchNext()) != 0 ) + { + %terrainId = %terrainObject.getId(); + %terrainName = %terrainObject.getName(); + if ( %terrainName $= "" ) + %terrainName = "Unnamed (" @ %terrainId @ ")"; + + TerrainSelectListBox.addItem( %terrainName, %terrainId ); + } +} + +function TerrainExportGui::init( %this ) +{ + %this.findAllTerrains(); +} + +function TerrainExportGui::export( %this ) +{ + %itemId = TerrainSelectListBox.getSelectedItem(); + %terrainObj = TerrainSelectListBox.getItemObject( %itemId ); + if ( !isObject( %terrainObj ) ) + { + MessageBoxOK( "Export failed", "Could not find the selected TerrainBlock!" ); + return; + } + + %filePath = SelectFolderTextEdit.getText(); + + %terrainName = %terrainObj.getName(); + if ( %terrainName $= "" ) + %terrainName = "Unnamed"; + + %fileName = %terrainName @ "_heightmap.png"; + %filePrefix = %terrainName @ "_layerMap"; + + %ret = %terrainObj.exportHeightMap( %filePath @ "/" @ %fileName, "png" ); + if ( %ret ) + %ret = %terrainObj.exportLayerMaps( %filePath @ "/" @ %filePrefix, "png" ); + + if ( %ret ) + %this.close(); +} + +function TerrainExportGui::onWake( %this ) +{ + TerrainExportGui.init(); +} + +function TerrainExportGui::close( %this ) +{ + Canvas.popDialog( %this ); +} + +function TerrainExportGui::showExportDialog( %this ) +{ + %this.findAllTerrains(); + + Canvas.pushDialog( %this ); +} + +function TerrainExportGui::openFolderCallback( %this, %path ) +{ + SelectFolderTextEdit.setText( %path ); +} + +function TerrainExportGui::selectFolder( %this ) +{ + %this.doOpenDialog( "", %this @ ".openFolderCallback" ); +} + +function TerrainExportGui::doOpenDialog( %this, %filter, %callback ) +{ + %dlg = new OpenFolderDialog() + { + Title = "Select Export Folder"; + Filters = %filter; + DefaultFile = %currentFile; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + if(filePath( %currentFile ) !$= "") + %dlg.DefaultPath = filePath(%currentFile); + else + %dlg.DefaultPath = getMainDotCSDir(); + + if(%dlg.Execute()) + eval(%callback @ "(\"" @ %dlg.FileName @ "\");"); + + %dlg.delete(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainImportGui.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainImportGui.gui new file mode 100644 index 000000000..c82e82450 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainImportGui.gui @@ -0,0 +1,759 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainImportGui, EditorGuiGroup) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiOverlayProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + internalName = "TerrainImport"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + Position = "119 84"; + Extent = "391 257"; + MinExtent = "391 257"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "1"; + AnchorLeft = "1"; + AnchorRight = "1"; + resizeWidth = "1"; + resizeHeight = "0"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "4 4"; + closeCommand = "Canvas.popDialog( TerrainImportGui );"; + EdgeSnap = "0"; + text = "Import Terrain Height Map"; + + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "HeightfieldFilename"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "10 85"; + Extent = "298 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = " "; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "11 66"; + Extent = "120 20"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Height Map Image:"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "316 83"; + Extent = "65 22"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "TerrainImportGui.browseForHeightfield();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Browse..."; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "MetersPerPixel"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "226 44"; + Extent = "82 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "1"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "226 26"; + Extent = "88 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Meters Per Pixel"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "316 26"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Height Scale:"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "HeightScale"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "316 44"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "256"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "10 112"; + Extent = "365 2"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/gui/images/separator-v"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "top"; + Position = "14 123"; + Extent = "74 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "1"; + text = "Texture Map"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "341 142"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "TerrainImportGui.browseForOpacityMap();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "+"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "341 165"; + Extent = "18 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "TerrainImportGui.removeOpacitymap();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "-"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "195 225"; + Extent = "88 24"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "TerrainImportGui.import();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Import"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + internalName = "OpacityLayerScroll"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "10 142"; + Extent = "326 75"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTextListCtrl() { + canSaveDynamicFields = "0"; + internalName = "OpacityLayerTextList"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTextListProfile"; + HorizSizing = "width"; + VertSizing = "top"; + Position = "1 1"; + Extent = "293 2"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + AltCommand = "TerrainImportGui.onOpacityListDblClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + enumerate = "0"; + resizeCell = "1"; + columns = "0 250 300"; + fitParentWidth = "1"; + clipColumnText = "1"; + }; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "264 123"; + Extent = "48 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Channels"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "11 26"; + Extent = "64 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Name:"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + canSaveDynamicFields = "0"; + internalName = "TerrainName"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "10 44"; + Extent = "206 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "theTerrain"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "341 199"; + Extent = "40 18"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "TerrainImportGui.onOpacityListDblClick();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + Position = "293 225"; + Extent = "88 24"; + MinExtent = "8 2"; + canSave = "1"; + isDecoy = "0"; + Visible = "1"; + Command = "Canvas.popDialog( TerrainImportGui );"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiCheckBoxCtrl() { + text = " Flip Y axis?"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "12 222"; + extent = "140 30"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "FlipYAxis"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- + + +function TerrainImportGui::onWake( %this ) +{ + if ( !isObject( %this.namesArray ) ) + %this.namesArray = new ArrayObject(); + + if ( !isObject( %this.channelsArray ) ) + %this.channelsArray = new ArrayObject(); +} + +function TerrainImportGui::import( %this ) +{ + // Gather all the import settings. + + %heightMapPng = %this-->HeightfieldFilename.getText(); + + %metersPerPixel = %this-->MetersPerPixel.getText(); + %heightScale = %this-->HeightScale.getText(); + + %flipYAxis = %this-->FlipYAxis.isStateOn(); + + // Grab and validate terrain object name. + + %terrainName = %this-->TerrainName.getText(); + if( !( isObject( %terrainName ) && %terrainName.isMemberOfClass( "TerrainBlock" ) ) && + !Editor::validateObjectName( %terrainName ) ) + return; + + %opacityNames = ""; + %materialNames = ""; + + %opacityList = %this-->OpacityLayerTextList; + + for( %i = 0; %i < %opacityList.rowCount(); %i++ ) + { + %itemText = %opacityList.getRowTextById( %i ); + %opacityName = %this.namesArray.getValue( %i ); + + %channelInfo = %this.channelsArray.getValue( %i ); + %channel = getWord( %channelInfo, 0 ); + + %materialName = getField( %itemText, 2 ); + + %opacityNames = %opacityNames @ %opacityName TAB %channel @ "\n"; + %materialNames = %materialNames @ %materialName @ "\n"; + } + + %updated = nameToID( %terrainName ); + + // This will update an existing terrain with the name %terrainName, + // or create a new one if %terrainName isn't a TerrainBlock + %obj = TerrainBlock::import( %terrainName, + %heightMapPng, + %metersPerPixel, + %heightScale, + %opacityNames, + %materialNames, + %flipYAxis ); + + Canvas.popDialog( %this ); + + if ( isObject( %obj ) ) + { + if( %obj != %updated ) + { + // created a new TerrainBlock + // Submit an undo action. + MECreateUndoAction::submit(%obj); + } + + assert( isObject( EWorldEditor ), + "ObjectBuilderGui::processNewObject - EWorldEditor is missing!" ); + + // Select it in the editor. + EWorldEditor.clearSelection(); + EWorldEditor.selectObject(%obj); + + // When we drop the selection don't store undo + // state for it... the creation deals with it. + EWorldEditor.dropSelection( true ); + + ETerrainEditor.isDirty = true; + EPainter.updateLayers(); + } + else + { + MessageBox( "Import Terrain", + "Terrain import failed! Check console for error messages.", + "Ok", "Error" ); + } +} + +function TerrainImportGui::doOpenDialog( %this, %filter, %callback ) +{ + %dlg = new OpenFileDialog() + { + Filters = %filter; + DefaultFile = %currentFile; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + if(filePath( %currentFile ) !$= "") + %dlg.DefaultPath = filePath(%currentFile); + else + %dlg.DefaultPath = getMainDotCSDir(); + + if(%dlg.Execute()) + eval(%callback @ "(\"" @ %dlg.FileName @ "\");"); + + + %dlg.delete(); +} + +function TerrainImportGui_SetHeightfield( %name ) +{ + TerrainImportGui-->HeightfieldFilename.setText( %name ); +} + +$TerrainImportGui::HeightFieldFilter = "Heightfield Files (*.png, *.bmp, *.jpg, *.gif)|*.png;*.bmp;*.jpg;*.gif|All Files (*.*)|*.*|"; +$TerrainImportGui::OpacityMapFilter = "Opacity Map Files (*.png, *.bmp, *.jpg, *.gif)|*.png;*.bmp;*.jpg;*.gif|All Files (*.*)|*.*|"; + +function TerrainImportGui::browseForHeightfield( %this ) +{ + %this.doOpenDialog( $TerrainImportGui::HeightFieldFilter, "TerrainImportGui_SetHeightfield" ); +} + +function TerrainImportGuiAddOpacityMap( %name ) +{ + // TODO: Need to actually look at + // the file here and figure + // out how many channels it has. + + %txt = makeRelativePath( %name, getWorkingDirectory() ); + + // Will need to do this stuff + // once per channel in the file + // currently it works with just grayscale. + %channelsTxt = "R" TAB "G" TAB "B" TAB "A"; + %bitmapInfo = getBitmapinfo( %name ); + + %channelCount = getWord( %bitmapInfo, 2 ); + + %opacityList = TerrainImportGui-->OpacityLayerTextList; + + for ( %i = 0; %i < %channelCount; %i++ ) + { + TerrainImportGui.namesArray.push_back( %txt, %name ); + TerrainImportGui.channelsArray.push_back( %txt, getWord( %channelsTxt, %i ) TAB %channelCount ); + + //TerrainImportGui.namesArray.echo(); + + %count = %opacityList.rowCount(); + %opacityList.addRow( %count, %txt TAB getWord( %channelsTxt, %i ) ); + } + + //OpacityMapListBox.addItem( %name ); +} + +function TerrainImportGui::browseForOpacityMap( %this ) +{ + TerrainImportGui.doOpenDialog( $TerrainImportGui::OpacityMapFilter, "TerrainImportGuiAddOpacityMap" ); +} + +function TerrainImportGui::removeOpacitymap( %this ) +{ + %opacityList = %this-->OpacityLayerTextList; + + //%itemIdx = OpacityMapListBox.getSelectedItem(); + %itemIdx = %opacityList.getSelectedId(); + if ( %itemIdx < 0 ) // -1 is no item selected + return; + + %this.namesArray.erase( %itemIdx ); + %this.channelsArray.erase( %itemIdx ); + + //%this.namesArray.echo(); + + %opacityList.removeRowById( %itemIdx ); + + //OpacityMapListBox.deleteItem( %itemIdx ); +} + +function TerrainImportGui::onOpacityListDblClick( %this ) +{ + %opacityList = %this-->OpacityLayerTextList; + + //echo( "Double clicked the opacity list control!" ); + %itemIdx = %opacityList.getSelectedId(); + if ( %itemIdx < 0 ) + return; + + %this.activeIdx = %itemIdx; + + %rowTxt = %opacityList.getRowTextById( %itemIdx ); + %matTxt = getField( %rowTxt, 2 ); + %matId = getField( %rowTxt, 3 ); + + TerrainMaterialDlg.showByObjectId( %matId, TerrainImportGui_TerrainMaterialApplyCallback ); +} + +function TerrainImportGui_TerrainMaterialApplyCallback( %mat, %matIndex ) +{ + // Skip over a bad selection. + if ( !isObject( %mat ) ) + return; + + %opacityList = TerrainImportGui-->OpacityLayerTextList; + + %itemIdx = TerrainImportGui.activeIdx; + + if ( %itemIdx < 0 || %itemIdx $= "" ) + return; + + %rowTxt = %opacityList.getRowTextById( %itemIdx ); + + %columntTxtCount = getFieldCount( %rowTxt ); + if ( %columntTxtCount > 2 ) + %rowTxt = getFields( %rowTxt, 0, 1 ); + + %opacityList.setRowById( %itemIdx, %rowTxt TAB %mat.internalName TAB %mat ); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui new file mode 100644 index 000000000..09a90ef26 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui @@ -0,0 +1,1392 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(TerrainMaterialDlg, EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 768"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "center"; + VertSizing = "center"; + position = "221 151"; + Extent = "394 432"; + MinExtent = "358 432"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "4 4 4 4"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "TerrainMaterialDlg.dialogCancel();"; + EdgeSnap = "0"; + text = "Terrain Materials Editor"; + new GuiContainer(){ //Node Properties + isContainer = "1"; + Profile = "inspectorStyleRolloutDarkProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "6 25"; + Extent = "189 64"; + + new GuiTextCtrl(){ + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "5 0"; + Extent = "91 18"; + text = "Terrain Materials"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "160 2"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.newMat();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/new"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "173 2"; + Extent = "15 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.deleteMat();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + }; + new GuiContainer() { + canSaveDynamicFields = "0"; + internalName = "matSettingsParent"; + isContainer = "1"; + Profile = "inspectorStyleRolloutProfile"; + HorizSizing = "left"; + VertSizing = "height"; + position = "202 26"; + Extent = "185 363"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "1 0"; + Extent = "183 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "core/art/gui/images/separator-v"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 22"; + Extent = "44 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiDefaultProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Name"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "matNameCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "39 21"; + Extent = "143 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + altCommand = "TerrainMaterialDlg.setMaterialName( $ThisControl.getText() );"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiInspectorTitleTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "8 0"; + Extent = "117 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Material Properties"; + maxLength = "1024"; + }; + new GuiContainer() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "6 43"; + Extent = "185 75"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiCheckBoxCtrl() { + internalName = "sideProjectionCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "55 54"; + Extent = "119 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = " Use Side Projection"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + new GuiBitmapCtrl() { + internalName = "baseTexCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "47 47"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeBase();"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the Active Diffuse Map for this layer"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "39 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Diffuse"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "116 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "116 0"; + Extent = "40 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeBase();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "159 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg-->baseTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "132 35"; + Extent = "39 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "Size"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "baseSizeCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "94 34"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + }; + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "6 116"; + Extent = "175 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/gui/images/separator-v"; + wrap = "0"; + }; + new GuiContainer() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "6 295"; + Extent = "185 50"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiBitmapCtrl() { + internalName = "normTexCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "47 47"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "39 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Normal"; + maxLength = "1024"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeNormal();"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Normal Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 15"; + Extent = "116 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "116 0"; + Extent = "40 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeNormal();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "159 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg-->normTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "92 34"; + Extent = "77 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Parallax Scale"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "parallaxScaleCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "55 33"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "0.00"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + }; + + new GuiBitmapCtrl() { + bitmap = "tools/gui/images/separator-v"; + wrap = "0"; + position = "6 288"; + extent = "175 2"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiContainer() { + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "6 122"; + extent = "185 72"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + + new GuiBitmapCtrl() { + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + position = "1 1"; + extent = "47 47"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "macroTexCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "1 1"; + extent = "48 48"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "TerrainMaterialDlg.changeMacro();"; + tooltipProfile = "ToolsGuiDefaultProfile"; + tooltip = "Change the active Macro Map for this layer."; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Macro"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "56 -3"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "EditorTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "None"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "56 17"; + extent = "116 17"; + minExtent = "8 2"; + horizSizing = "width"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiButtonCtrl() { + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "116 0"; + extent = "40 16"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ToolsGuiButtonProfile"; + visible = "1"; + active = "1"; + command = "TerrainMaterialDlg.changeMacro();"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + bitmapMode = "Stretched"; + autoFitExtents = "0"; + useModifiers = "0"; + useStates = "1"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + position = "159 0"; + extent = "16 16"; + minExtent = "8 2"; + horizSizing = "left"; + vertSizing = "bottom"; + profile = "ToolsGuiDefaultProfile"; + visible = "1"; + active = "1"; + command = "TerrainMaterialDlg-->macroTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Size"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "132 33"; + extent = "39 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "94 32"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "macroSizeCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Strength"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "39 54"; + extent = "46 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "1 53"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "macroStrengthCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Distance"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "132 54"; + extent = "45 16"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "0"; + anchorBottom = "0"; + anchorLeft = "0"; + anchorRight = "0"; + position = "94 53"; + extent = "34 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextEditProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + internalName = "macroDistanceCtrl"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + }; + + new GuiBitmapCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "6 200"; + Extent = "175 2"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/gui/images/separator-v"; + wrap = "0"; + }; + new GuiContainer() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "6 206"; + Extent = "185 72"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiBitmapCtrl() { + internalName = "detailTexCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "47 47"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + bitmap = "tools/materialEditor/gui/unknownImage"; + wrap = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "48 48"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeDetail();"; + tooltipprofile = "ToolsGuiDefaultProfile"; + ToolTip = "Change the active Detail Map for this layer."; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/materialEditor/gui/cubemapBtnBorder"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "EditorTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "56 -3"; + Extent = "30 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Detail"; + maxLength = "1024"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "56 16"; + Extent = "116 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "None"; + maxLength = "1024"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "116 0"; + Extent = "40 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.changeDetail();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Edit"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "159 0"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg-->detailTexCtrl.setBitmap(\"tools/materialEditor/gui/unknownImage\");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + bitmap = "tools/gui/images/delete"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "132 33"; + Extent = "39 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "Size"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "detSizeCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "94 32"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "39 54"; + Extent = "46 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "Strength"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "detStrengthCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 53"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "132 54"; + Extent = "45 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + text = "Distance"; + maxLength = "1024"; + }; + new GuiTextEditCtrl() { + internalName = "detDistanceCtrl"; + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "94 53"; + Extent = "34 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "0"; + AnchorBottom = "0"; + AnchorLeft = "0"; + AnchorRight = "0"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "*"; + }; + }; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "6 42"; + Extent = "189 373"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "189 374"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = "false"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + mouseWheelScrollSpeed = "-1"; + + new GuiTreeViewCtrl() { + internalName = "matLibTree"; + canSaveDynamicFields = "0"; + class = "TerrainMaterialTreeCtrl"; + className = "TerrainMaterialTreeCtrl"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "1 1"; + Extent = "125 84"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "0"; + MultipleSelections = "0"; + DeleteObjectAllowed = "0"; + DragToItemAllowed = "0"; + ClearAllOnSingleSelection = "1"; + showRoot = "0"; + internalNamesOnly = "1"; + objectNamesOnly = "0"; + }; + }; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "202 394"; + Extent = "98 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.dialogApply();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Apply&Select"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "top"; + position = "307 394"; + Extent = "80 22"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "TerrainMaterialDlg.dialogCancel();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = "Cancel"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + + new GuiBitmapCtrl() { // inactive overlay + internalName = "inactiveOverlay"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "height"; + position = "199 23"; + Extent = "190 267"; + isContainer = true; + Visible = false; + bitmap = "tools/gui/images/inactive-overlay"; + + new GuiTextCtrl(){ + internalName = "inactiveOverlayDlg"; + Profile = "ToolsGuiTextCenterProfile"; + HorizSizing = "width"; + VertSizing = "center"; + position = "0 104"; + Extent = "190 64"; + text = "Inactive"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorCreatorWindow.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorCreatorWindow.ed.gui new file mode 100644 index 000000000..03a3d6d8a --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorCreatorWindow.ed.gui @@ -0,0 +1,286 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(WorldEditorCreatorWindowContainer) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiWindowCtrl(EWCreatorWindow) { + canSaveDynamicFields = "0"; + internalName = "CreatorWindow"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "38 36"; + Extent = "354 276"; + MinExtent = "354 207"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "30 30"; + EdgeSnap = "0"; + text = "Creator"; + + new GuiContainer() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "2 80"; + Extent = "348 195"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "4 4 4 4"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "EditorLightScrollProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 4"; + Extent = "340 187"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "Client"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "alwaysOff"; + lockHorizScroll = "false"; + lockVertScroll = "true"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiDynamicCtrlArrayControl(CreatorIconArray) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTransparentProfile"; + HorizSizing = "width"; + VertSizing = "height"; + Position = "2 2"; + Extent = "335 183"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + colCount = "9"; + colSize = "64"; + rowCount = "0"; + RowSize = "64"; + rowSpacing = "4"; + colSpacing = "4"; + frozen = "0"; + autoCellSize = "1"; + fillRowFirst = "0"; + dynamicSize = "1"; + }; + }; + }; + new GuiControl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 24"; + Extent = "348 55"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + + new GuiBitmapButtonCtrl(CreatorNavUpButton) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + Position = "319 33"; + Extent = "20 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EWCreatorWindow.navigateUp();"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + groupNum = "-1"; + buttonType = "PushButton"; + + Bitmap = "tools/gui/images/folderUp.png"; + autoSize = "0"; + }; + new GuiPopUpMenuCtrl(CreatorPopupMenu) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 33"; + Extent = "308 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Objects"; + maxLength = "1024"; + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + }; + new GuiTabBookCtrl(CreatorTabBook) { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabBookProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + Position = "4 4"; + Extent = "342 21"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Docking = "None"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + TabPosition = "Top"; + TabMargin = "7"; + MinTabWidth = "64"; + + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Shapes"; + maxLength = "1024"; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "0"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "Geometry"; + maxLength = "1024"; + }; + new GuiTabPageCtrl() { + canSaveDynamicFields = "0"; + Enabled = "1"; + isContainer = "1"; + Profile = "ToolsGuiTabPageProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "0 0"; + MinExtent = "0 0"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + text = "MissionObjects"; + maxLength = "1024"; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorMissionInspector.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorMissionInspector.ed.gui new file mode 100644 index 000000000..ab7bfe2f5 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/guiWorldEditorMissionInspector.ed.gui @@ -0,0 +1,299 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(WorldEditorMissionInspector,EditorGuiGroup) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "800 600"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiWindowCtrl(EWInspectorWindow) { + canSaveDynamicFields = "0"; + internalName = "InspectorWindow"; + isContainer = "1"; + Profile = "ToolsGuiWindowProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "333 26"; + Extent = "304 448"; + MinExtent = "304 448"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + text = "Mission Inspector"; + maxLength = "1024"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "1"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + closeCommand = "EWInspectorFrame.parentGroup.setVisible(false);"; + + new GuiFrameSetCtrl(EWInspectorFrame) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiFrameSetProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "8 32"; + Extent = "288 408"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + columns = "0"; + rows = "0 206"; + borderWidth = "0"; + borderColor = "84 12 136 1"; + borderEnable = "dynamic"; + borderMovable = "dynamic"; + autoBalance = "0"; + fudgeFactor = "0"; + + new GuiControl(EWTreePane) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "EditorDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "288 202"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "288 202"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "alwaysOff"; + vScrollBar = "dynamic"; + lockHorizScroll = "true"; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTreeViewCtrl(EditorTree) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "226 21"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + }; + }; + }; + new GuiControl(EWCreatorInspectorPane) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "EditorDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 206"; + Extent = "288 202"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiScrollCtrl(EWCreatorPane) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "288 202"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "0"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiTreeViewCtrl(Creator) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiTreeViewProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "131 84"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + tabSize = "16"; + textOffset = "2"; + fullRowSelect = "0"; + itemHeight = "21"; + destroyTreeOnSleep = "1"; + MouseDragging = "1"; + MultipleSelections = "1"; + DeleteObjectAllowed = "1"; + DragToItemAllowed = "1"; + }; + }; + new GuiControl(EWInspectorPane) { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "EditorDefaultProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 0"; + Extent = "288 202"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiControl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiSolidDefaultProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "288 24"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + + new GuiButtonCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "40 20"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + Command = "EWorldEditor.isDirty = true;EWorldEditor.makeFirstResponder(true);"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Don\'t forget to hit Apply after making changes!"; + hovertime = "1000"; + text = "Apply"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + }; + new GuiTextCtrl() { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiInspectorFieldProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "52 4"; + Extent = "42 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + text = "Name:"; + maxLength = "1024"; + }; + new GuiTextEditCtrl(InspectorNameEdit) { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "GuiInspectorBackgroundProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "97 4"; + Extent = "758 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + AltCommand = "EWorldEditor.isDirty = true;"; + hovertime = "1000"; + maxLength = "1024"; + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + }; + }; + new GuiScrollCtrl() { + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "ToolsGuiScrollProfile"; + HorizSizing = "width"; + VertSizing = "height"; + position = "0 24"; + Extent = "288 178"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + willFirstRespond = "1"; + hScrollBar = "dynamic"; + vScrollBar = "dynamic"; + lockHorizScroll = true; + lockVertScroll = "false"; + constantThumbHeight = "0"; + childMargin = "0 0"; + + new GuiInspector(Inspector) { + StackingType = "Vertical"; + HorizStacking = "Left to Right"; + VertStacking = "Top to Bottom"; + Padding = "1"; + canSaveDynamicFields = "0"; + isContainer = "1"; + Profile = "GuiInspectorBackgroundProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "266 8"; + MinExtent = "8 8"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + }; + }; + }; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui new file mode 100644 index 000000000..74f1200c6 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -0,0 +1,1156 @@ +//--- OBJECT WRITE BEGIN --- +%guiContent = new GuiControl(ObjectBuilderGui, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "800 600"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + + new GuiWindowCtrl(OBTargetWindow) { + profile = "ToolsGuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "384 205"; + extent = "256 282"; + minExtent = "256 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = "Create Object"; + + new GuiTextCtrl() { + profile = "GuiCenterTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "9 26"; + extent = "84 16"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = "Object Name:"; + }; + new GuiTextEditCtrl(OBObjectName) { + class = ObjectBuilderGuiTextEditCtrl; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "78 26"; + extent = "172 18"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + historySize = "0"; + }; + new GuiBitmapBorderCtrl(OBContentWindow) { + profile = "ToolsGuiGroupBorderProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "7 51"; + extent = "243 193"; + minExtent = "0 0"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + }; + new GuiButtonCtrl(OBOKButton) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "7 250"; + extent = "156 24"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + command = "ObjectBuilderGui.onOK();"; + helpTag = "0"; + text = "Create New"; + Accelerator = "return"; + }; + new GuiButtonCtrl(OBCancelButton) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "170 250"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + command = "ObjectBuilderGui.onCancel();"; + helpTag = "0"; + text = "Cancel"; + Accelerator = "escape"; + }; + }; +}; +//--- OBJECT WRITE END --- + +function ObjectBuilderGui::init(%this) +{ + %this.baseOffsetX = 5; + %this.baseOffsetY = 5; + %this.defaultObjectName = ""; + %this.defaultFieldStep = 22; + %this.columnOffset = 110; + + %this.fieldNameExtent = "105 18"; + %this.textEditExtent = "122 18"; + %this.checkBoxExtent = "13 18"; + %this.popupMenuExtent = "122 18"; + %this.fileButtonExtent = "122 18"; + %this.matButtonExtent = "17 18"; + + // + %this.numControls = 0; + + %this.lastPath = ""; + + %this.reset(); +} + +function ObjectBuilderGui::reset(%this) +{ + %this.objectGroup = ""; + %this.curXPos = %this.baseOffsetX; + %this.curYPos = %this.baseOffsetY; + %this.createFunction = ""; + %this.createCallback = ""; + %this.currentControl = 0; + + // + OBObjectName.setValue(%this.defaultObjectName); + + // + %this.newObject = 0; + %this.objectClassName = ""; + %this.numFields = 0; + + // + for(%i = 0; %i < %this.numControls; %i++) + { + %this.textControls[%i].delete(); + %this.controls[%i].delete(); + } + %this.numControls = 0; +} + +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::createFileType(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::createFileType: invalid field"); + return; + } + + // + if(%this.field[%index, text] $= "") + %name = %this.field[%index, name]; + else + %name = %this.field[%index, text]; + + // + %this.textControls[%this.numControls] = new GuiTextCtrl() { + profile = "ToolsGuiTextRightProfile"; + text = %name; + extent = %this.fieldNameExtent; + position = %this.curXPos @ " " @ %this.curYPos; + modal = "1"; + }; + + // + %this.controls[%this.numControls] = new GuiButtonCtrl() { + HorizSizing = "width"; + profile = "ToolsGuiButtonProfile"; + extent = %this.fileButtonExtent; + position = %this.curXPos + %this.columnOffset @ " " @ %this.curYPos; + modal = "1"; + command = %this @ ".getFileName(" @ %index @ ");"; + }; + + %val = %this.field[%index, value]; + %this.controls[%this.numControls].setValue(fileBase(%val) @ fileExt(%val)); + + %this.numControls++; + %this.curYPos += %this.defaultFieldStep; +} + +function ObjectBuilderGui::getFileName(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::getFileName: invalid field"); + return; + } + + %val = %this.field[%index, ext]; + + //%path = filePath(%val); + //%ext = fileExt(%val); + + %this.currentControl = %index; + getLoadFilename( %val @ "|" @ %val, %this @ ".gotFileName", %this.lastPath ); +} + +function ObjectBuilderGui::gotFileName(%this, %name) +{ + %index = %this.currentControl; + + %name = makeRelativePath(%name,getWorkingDirectory()); + + %this.field[%index, value] = %name; + %this.controls[%this.currentControl].setText(fileBase(%name) @ fileExt(%name)); + + %this.lastPath = %name; + + // This doesn't work for button controls as getValue returns their state! + //%this.controls[%this.currentControl].setValue(%name); +} + +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::createMaterialNameType(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::createMaterialNameType: invalid field"); + return; + } + + // + if(%this.field[%index, text] $= "") + %name = %this.field[%index, name]; + else + %name = %this.field[%index, text]; + + // + %this.textControls[%this.numControls] = new GuiTextCtrl() { + profile = "ToolsGuiTextRightProfile"; + text = %name; + extent = %this.fieldNameExtent; + position = %this.curXPos @ " " @ %this.curYPos; + modal = "1"; + }; + + // + %this.controls[%this.numControls] = new GuiControl() { + HorizSizing = "width"; + profile = "ToolsGuiDefaultProfile"; + extent = %this.textEditExtent; + position = %this.curXPos + %this.columnOffset @ " " @ %this.curYPos; + modal = "1"; + }; + + %text = new GuiTextEditCtrl() { + class = ObjectBuilderGuiTextEditCtrl; + internalName = "MatText"; + HorizSizing = "width"; + profile = "ToolsGuiTextEditProfile"; + extent = getWord(%this.textEditExtent,0) - getWord(%this.matButtonExtent,0) - 2 @ " " @ getWord(%this.textEditExtent,1); + text = %this.field[%index, value]; + position = "0 0"; + modal = "1"; + }; + %this.controls[%this.numControls].addGuiControl(%text); + + %button = new GuiBitmapButtonCtrl() { + internalName = "MatButton"; + HorizSizing = "left"; + profile = "ToolsGuiButtonProfile"; + extent = %this.matButtonExtent; + position = getWord(%this.textEditExtent,0) - getWord(%this.matButtonExtent,0) @ " 0"; + modal = "1"; + command = %this @ ".getMaterialName(" @ %index @ ");"; + }; + %button.setBitmap("tools/materialEditor/gui/change-material-btn"); + %this.controls[%this.numControls].addGuiControl(%button); + + //%val = %this.field[%index, value]; + //%this.controls[%this.numControls].setValue(%val); + //%this.controls[%this.numControls].setBitmap("tools/materialEditor/gui/change-material-btn"); + + %this.numControls++; + %this.curYPos += %this.defaultFieldStep; +} + +function ObjectBuilderGui::getMaterialName(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::getMaterialName: invalid field"); + return; + } + + %this.currentControl = %index; + materialSelector.showDialog(%this @ ".gotMaterialName", "name"); +} + +function ObjectBuilderGui::gotMaterialName(%this, %name) +{ + %index = %this.currentControl; + + %this.field[%index, value] = %name; + %this.controls[%index]-->MatText.setText(%name); +} + +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::createDataBlockType(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::createDataBlockType: invalid field"); + return; + } + + // + if(%this.field[%index, text] $= "") + %name = %this.field[%index, name]; + else + %name = %this.field[%index, text]; + + // + %this.textControls[%this.numControls] = new GuiTextCtrl() { + profile = "ToolsGuiTextRightProfile"; + text = %name; + extent = %this.fieldNameExtent; + position = %this.curXPos @ " " @ %this.curYPos; + modal = "1"; + }; + + // + %this.controls[%this.numControls] = new GuiPopupMenuCtrl() { + HorizSizing = "width"; + profile = "ToolsGuiPopUpMenuProfile"; + extent = %this.popupMenuExtent; + position = %this.curXPos + %this.columnOffset @ " " @ %this.curYPos; + modal = "1"; + maxPopupHeight = "200"; + }; + + %classname = getWord(%this.field[%index, value], 0); + %classname_alt = getWord(%this.field[%index, value], 1); + + %this.controls[%this.numControls].add("", -1); + + // add the datablocks + for(%i = 0; %i < DataBlockGroup.getCount(); %i++) + { + %obj = DataBlockGroup.getObject(%i); + if( isMemberOfClass( %obj.getClassName(), %classname ) || isMemberOfClass ( %obj.getClassName(), %classname_alt ) ) + %this.controls[%this.numControls].add(%obj.getName(), %i); + } + + %this.controls[%this.numControls].setValue(getWord(%this.field[%index, value], 1)); + + %this.numControls++; + %this.curYPos += %this.defaultFieldStep; +} + +function ObjectBuilderGui::createBoolType(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::createBoolType: invalid field"); + return; + } + + // + if(%this.field[%index, value] $= "") + %value = 0; + else + %value = %this.field[%index, value]; + + // + if(%this.field[%index, text] $= "") + %name = %this.field[%index, name]; + else + %name = %this.field[%index, text]; + + // + %this.textControls[%this.numControls] = new GuiTextCtrl() { + profile = "ToolsGuiTextRightProfile"; + text = %name; + extent = %this.fieldNameExtent; + position = %this.curXPos @ " " @ %this.curYPos; + modal = "1"; + }; + + // + %this.controls[%this.numControls] = new GuiCheckBoxCtrl() { + profile = "ToolsGuiCheckBoxProfile"; + extent = %this.checkBoxExtent; + position = %this.curXPos + %this.columnOffset @ " " @ %this.curYPos; + modal = "1"; + }; + + %this.controls[%this.numControls].setValue(%value); + + %this.numControls++; + %this.curYPos += %this.defaultFieldStep; +} + +function ObjectBuilderGuiTextEditCtrl::onGainFirstResponder(%this) +{ + %this.selectAllText(); +} + +function ObjectBuilderGui::createStringType(%this, %index) +{ + if(%index >= %this.numFields || %this.field[%index, name] $= "") + { + error("ObjectBuilderGui::createStringType: invalid field"); + return; + } + + // + if(%this.field[%index, text] $= "") + %name = %this.field[%index, name]; + else + %name = %this.field[%index, text]; + + // + %this.textControls[%this.numControls] = new GuiTextCtrl() { + profile = "ToolsGuiTextRightProfile"; + text = %name; + extent = %this.fieldNameExtent; + position = %this.curXPos @ " " @ %this.curYPos; + modal = "1"; + }; + + // + %this.controls[%this.numControls] = new GuiTextEditCtrl() { + class = ObjectBuilderGuiTextEditCtrl; + HorizSizing = "width"; + profile = "ToolsGuiTextEditProfile"; + extent = %this.textEditExtent; + text = %this.field[%index, value]; + position = %this.curXPos + %this.columnOffset @ " " @ %this.curYPos; + modal = "1"; + }; + + %this.numControls++; + %this.curYPos += %this.defaultFieldStep; +} + +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::adjustSizes(%this) +{ + if(%this.numControls == 0) + %this.curYPos = 0; + + OBTargetWindow.extent = getWord(OBTargetWindow.extent, 0) SPC %this.curYPos + 88; + OBContentWindow.extent = getWord(OBContentWindow.extent, 0) SPC %this.curYPos; + OBOKButton.position = getWord(OBOKButton.position, 0) SPC %this.curYPos + 57; + OBCancelButton.position = getWord(OBCancelButton.position, 0) SPC %this.curYPos + 57; +} + +function ObjectBuilderGui::process(%this) +{ + if(%this.objectClassName $= "") + { + error("ObjectBuilderGui::process: classname is not specified"); + return; + } + + OBTargetWindow.text = "Create Object: " @ %this.objectClassName; + + // + for(%i = 0; %i < %this.numFields; %i++) + { + switch$(%this.field[%i, type]) + { + case "TypeBool": + %this.createBoolType(%i); + + case "TypeDataBlock": + %this.createDataBlockType(%i); + + case "TypeFile": + %this.createFileType(%i); + + case "TypeMaterialName": + %this.createMaterialNameType(%i); + + default: + %this.createStringType(%i); + } + } + + // add the controls + for(%i = 0; %i < %this.numControls; %i++) + { + OBContentWindow.add(%this.textControls[%i]); + OBContentWindow.add(%this.controls[%i]); + } + + // + %this.adjustSizes(); + + // + Canvas.pushDialog(%this); +} + +function ObjectBuilderGui::processNewObject(%this, %obj) +{ + if ( %this.createCallback !$= "" ) + eval( %this.createCallback ); + + // Skip out if nothing was created. + if ( !isObject( %obj ) ) + return; + + // Add the object to the group. + if( %this.objectGroup !$= "" ) + %this.objectGroup.add( %obj ); + else + EWCreatorWindow.objectGroup.add( %obj ); + + // If we were given a callback to call after the + // object has been created, do so now. Also clear + // the callback to make sure it's valid only for + // a single invocation. + + %callback = %this.newObjectCallback; + %this.newObjectCallback = ""; + + if( %callback !$= "" ) + eval( %callback @ "( " @ %obj @ " );" ); +} + +function ObjectBuilderGui::onOK(%this) +{ + // Error out if the given object name is not valid or not unique. + %objectName = OBObjectName.getValue(); + if( !Editor::validateObjectName( %objectName, false )) + return; + + // get current values + for(%i = 0; %i < %this.numControls; %i++) + { + // uses button type where getValue returns button state! + if (%this.field[%i, type] $= "TypeFile") + { + if (strchr(%this.field[%i, value],"*") !$= "") + %this.field[%i, value] = ""; + + continue; + } + if (%this.field[%i, type] $= "TypeMaterialName") + { + %this.field[%i, value] = %this.controls[%i]-->MatText.getValue(); + continue; + } + %this.field[%i, value] = %this.controls[%i].getValue(); + } + + // If we have a special creation function then + // let it do the construction. + if ( %this.createFunction !$= "" ) + eval( %this.createFunction ); + + else + { + // Else we use the memento. + %memento = %this.buildMemento(); + eval( %memento ); + } + + if(%this.newObject != 0) + %this.processNewObject(%this.newObject); + + %this.reset(); + Canvas.popDialog(%this); +} + +function ObjectBuilderGui::onCancel(%this) +{ + %this.reset(); + Canvas.popDialog(%this); +} + +function ObjectBuilderGui::addField(%this, %name, %type, %text, %value, %ext) +{ + %this.field[%this.numFields, name] = %name; + %this.field[%this.numFields, type] = %type; + %this.field[%this.numFields, text] = %text; + %this.field[%this.numFields, value] = %value; + %this.field[%this.numFields, ext] = %ext; + + %this.numFields++; +} + +function ObjectBuilderGui::buildMemento(%this) +{ + // Build the object into a string. + %this.memento = %this @ ".newObject = new " @ %this.objectClassName @ "(" @ OBObjectName.getValue() @ ") { "; + for( %i = 0; %i < %this.numFields; %i++ ) + %this.memento = %this.memento @ %this.field[%i, name] @ " = \"" @ %this.field[%i, value] @ "\"; "; + %this.memento = %this.memento @ "};"; + + return %this.memento; +} + +//------------------------------------------------------------------------------ +// This function is used for objects that don't require any special +// fields/functionality when being built +//------------------------------------------------------------------------------ +function ObjectBuilderGui::buildObject(%this, %className) +{ + %this.objectClassName = %className; + %this.process(); +} + +//------------------------------------------------------------------------------ +// Environment +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::buildScatterSky( %this, %dontWarnAboutSun ) +{ + if( !%dontWarnAboutSun ) + { + // Check for sun object already in the level. If there is one, + // warn the user. + + initContainerTypeSearch( $TypeMasks::EnvironmentObjectType ); + while( 1 ) + { + %object = containerSearchNext(); + if( !%object ) + break; + + if( %object.isMemberOfClass( "Sun" ) ) + { + MessageBoxYesNo( "Warning", + "A ScatterSky object will conflict with the Sun object that is already in the level." SPC + "Do you still want to create a ScatterSky object?", + %this @ ".buildScatterSky( true );" ); + return; + } + } + } + + %this.objectClassName = "ScatterSky"; + + %this.addField("rayleighScattering", "TypeFloat", "Rayleigh Scattering", "0.0035"); + %this.addField("mieScattering", "TypeFloat", "Mie Scattering", "0.0045"); + %this.addField("skyBrightness", "TypeFloat", "Sky Brightness", "25"); + + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addField( "flareType", "TypeLightFlareDataPtr", "Flare", "ScatterSkyFlareExample" ); + %this.addField( "moonMat", "TypeMaterialName", "Moon Material", "Moon_Glow_Mat" ); + %this.addField( "nightCubemap", "TypeCubemapName", "Night Cubemap", "NightCubemap" ); + %this.addField( "useNightCubemap", "TypeBool", "Use Night Cubemap", "true" ); + +} + +function ObjectBuilderGui::buildCloudLayer(%this) +{ + OBObjectName.setValue( "" ); + %this.objectClassName = "CloudLayer"; + %this.addField( "texture", "TypeImageFilename", "Texture", "art/skies/clouds/clouds_normal_displacement" ); + %this.process(); +} + +function ObjectBuilderGui::buildBasicClouds(%this) +{ + OBObjectName.setValue( "" ); + %this.objectClassName = "BasicClouds"; + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addField( "texture[0]", "TypeImageFilename", "Texture", "art/skies/clouds/cloud1" ); + %this.addField( "texture[1]", "TypeImageFilename", "Texture", "art/skies/clouds/cloud2" ); + %this.addField( "texture[2]", "TypeImageFilename", "Texture", "art/skies/clouds/cloud3" ); +} + +function ObjectBuilderGui::checkExists( %this, %classname ) +{ + for ( %i = 0; %i < EWCreatorWindow.objectGroup.getCount(); %i++ ) + { + %obj = EWCreatorWindow.objectGroup.getObject( %i ); + if ( %obj.getClassName() $= %classname ) + return true; + } + + return false; +} + +function ObjectBuilderGui::buildsgMissionLightingFilter(%this) +{ + %this.objectClassName = "sgMissionLightingFilter"; + %this.addField("dataBlock", "TypeDataBlock", "sgMissionLightingFilter Data", "sgMissionFilterData"); + %this.process(); +} + +function ObjectBuilderGui::buildsgDecalProjector(%this) +{ + %this.objectClassName = "sgDecalProjector"; + %this.addField("dataBlock", "TypeDataBlock", "DecalData Data", "DecalData"); + %this.process(); +} + +function ObjectBuilderGui::buildsgLightObject(%this) +{ + %this.objectClassName = "sgLightObject"; + %this.addField("dataBlock", "TypeDataBlock", "LightObject Data", "sgLightObjectData"); + %this.process(); +} + +function ObjectBuilderGui::buildSun( %this, %dontWarnAboutScatterSky ) +{ + if( !%dontWarnAboutScatterSky ) + { + // Check for scattersky object already in the level. If there is one, + // warn the user. + + initContainerTypeSearch( $TypeMasks::EnvironmentObjectType ); + while( 1 ) + { + %object = containerSearchNext(); + if( !%object ) + break; + + if( %object.isMemberOfClass( "ScatterSky" ) ) + { + MessageBoxYesNo( "Warning", + "A Sun object will conflict with the ScatterSky object that is already in the level." SPC + "Do you still want to create a Sun object?", + %this @ ".buildSun( true );" ); + return; + } + } + } + + %this.objectClassName = "Sun"; + + %this.addField("direction", "TypeVector", "Direction", "1 1 -1"); + %this.addField("color", "TypeColor", "Sun color", "0.8 0.8 0.8"); + %this.addField("ambient", "TypeColor", "Ambient color", "0.2 0.2 0.2"); + + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addField( "coronaMaterial", "TypeMaterialName", "Corona Material", "Corona_Mat" ); + %this.addField( "flareType", "TypeLightFlareDataPtr", "Flare", "SunFlareExample" ); +} + +function ObjectBuilderGui::buildLightning(%this) +{ + %this.objectClassName = "Lightning"; + + %this.addField("dataBlock", "TypeDataBlock", "Data block", "LightningData DefaultStorm"); + + %this.process(); +} + +function ObjectBuilderGui::addWaterObjectFields(%this) +{ + %this.addField("rippleDir[0]", "TypePoint2", "Ripple Direction", "0.000000 1.000000"); + %this.addField("rippleDir[1]", "TypePoint2", "Ripple Direction", "0.707000 0.707000"); + %this.addField("rippleDir[2]", "TypePoint2", "Ripple Direction", "0.500000 0.860000"); + %this.addField("rippleTexScale[0]", "TypePoint2", "Ripple Texture Scale", "7.140000 7.140000"); + %this.addField("rippleTexScale[1]", "TypePoint2", "Ripple Texture Scale", "6.250000 12.500000"); + %this.addField("rippleTexScale[2]", "TypePoint2", "Ripple Texture Scale", "50.000000 50.000000"); + %this.addField("rippleSpeed[0]", "TypeFloat", "Ripple Speed", "0.065"); + %this.addField("rippleSpeed[1]", "TypeFloat", "Ripple Speed", "0.09"); + %this.addField("rippleSpeed[2]", "TypeFloat", "Ripple Speed", "0.04"); + %this.addField("rippleMagnitude[0]", "TypeFloat", "Ripple Magnitude", "1.0"); + %this.addField("rippleMagnitude[1]", "TypeFloat", "Ripple Magnitude", "1.0"); + %this.addField("rippleMagnitude[2]", "TypeFloat", "Ripple Magnitude", "0.3"); + %this.addField("overallRippleMagnitude", "TypeFloat", "Overall Ripple Magnitude", "1.0"); + + %this.addField("waveDir[0]", "TypePoint2", "Wave Direction", "0.000000 1.000000"); + %this.addField("waveDir[1]", "TypePoint2", "Wave Direction", "0.707000 0.707000"); + %this.addField("waveDir[2]", "TypePoint2", "Wave Direction", "0.500000 0.860000"); + %this.addField("waveMagnitude[0]", "TypePoint2", "Wave Magnitude", "0.2"); + %this.addField("waveMagnitude[1]", "TypePoint2", "Wave Magnitude", "0.2"); + %this.addField("waveMagnitude[2]", "TypePoint2", "Wave Magnitude", "0.2"); + %this.addField("waveSpeed[0]", "TypeFloat", "Wave Speed", "1"); + %this.addField("waveSpeed[1]", "TypeFloat", "Wave Speed", "1"); + %this.addField("waveSpeed[2]", "TypeFloat", "Wave Speed", "1"); + %this.addField("overallWaveMagnitude", "TypeFloat", "Overall Wave Magnitude", "1.0"); + + %this.addField("rippleTex", "TypeImageFilename", "Ripple Texture", "art/water/ripple" ); + %this.addField("depthGradientTex", "TypeImageFilename", "Depth Gradient Texture", "art/water/depthcolor_ramp" ); + %this.addField("foamTex", "TypeImageFilename", "Foam Texture", "art/water/foam" ); +} + +function ObjectBuilderGui::buildWaterBlock(%this) +{ + %this.objectClassName = "WaterBlock"; + %this.addField( "baseColor", "TypeColorI", "Base Color", "45 108 171 255" ); + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addWaterObjectFields(); +} + +function ObjectBuilderGui::buildWaterPlane(%this) +{ + %this.objectClassName = "WaterPlane"; + %this.addField( "baseColor", "TypeColorI", "Base Color", "45 108 171 255" ); + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addWaterObjectFields(); +} + +function ObjectBuilderGui::buildTerrainBlock(%this) +{ + %this.objectClassName = "TerrainBlock"; + %this.createCallback = "ETerrainEditor.attachTerrain();"; + + %this.addField("terrainFile", "TypeFile", "Terrain file", "", "*.ter"); + %this.addField("squareSize", "TypeInt", "Square size", "8"); + + %this.process(); +} + +function ObjectBuilderGui::buildGroundCover( %this ) +{ + %this.objectClassName = "GroundCover"; + %this.addField( "material", "TypeMaterialName", "Material Name", "" ); + %this.addField( "shapeFilename[0]", "TypeFile", "Shape File [Optional]", "", "*.*"); + %this.process(); + + // This is a trick... any fields added after process won't show + // up as controls, but will be applied to the created object. + %this.addField( "probability[0]", "TypeFloat", "Probability", "1" ); +} + +function ObjectBuilderGui::buildPrecipitation(%this) +{ + %this.objectClassName = "Precipitation"; + %this.addField("dataBlock", "TypeDataBlock", "Precipitation data", "PrecipitationData"); + %this.process(); +} + +function ObjectBuilderGui::buildParticleEmitterNode(%this) +{ + %this.objectClassName = "ParticleEmitterNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "ParticleEmitterNodeData"); + %this.addField("emitter", "TypeDataBlock", "Particle data", "ParticleEmitterData"); + %this.process(); +} + +function ObjectBuilderGui::buildRibbonNode(%this) +{ + %this.objectClassName = "RibbonNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "RibbonNodeData"); + %this.addField("ribbon", "TypeDataBlock", "Ribbon data", "RibbonData"); + %this.process(); +} + +function ObjectBuilderGui::buildParticleSimulation(%this) +{ + %this.objectClassName = "ParticleSimulation"; + %this.addField("datablock", "TypeDataBlock", "datablock", "ParticleSimulationData"); + %this.process(); +} + +//------------------------------------------------------------------------------ +// Mission +//------------------------------------------------------------------------------ + +function ObjectBuilderGui::buildTrigger(%this) +{ + %this.objectClassName = "Trigger"; + %this.addField("dataBlock", "TypeDataBlock", "Data Block", "TriggerData defaultTrigger"); + %this.addField("polyhedron", "TypeTriggerPolyhedron", "Polyhedron", "-0.5 0.5 0.0 1.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 1.0"); + %this.process(); +} + +function ObjectBuilderGui::buildPhysicalZone(%this) +{ + %this.objectClassName = "PhysicalZone"; + %this.addField("polyhedron", "TypeTriggerPolyhedron", "Polyhedron", "-0.5 0.5 0.0 1.0 0.0 0.0 0.0 -1.0 0.0 0.0 0.0 1.0"); + %this.process(); +} + +function ObjectBuilderGui::buildCamera(%this) +{ + %this.objectClassName = "Camera"; + + %this.addField("position", "TypePoint3", "Position", "0 0 0"); + %this.addField("rotation", "TypePoint4", "Rotation", "1 0 0 0"); + %this.addField("dataBlock", "TypeDataBlock", "Data block", "CameraData Observer"); + %this.addField("team", "TypeInt", "Team", "0"); + + %this.process(); +} + +function ObjectBuilderGui::buildLevelInfo(%this) +{ + if ( %this.checkExists( "LevelInfo" ) ) + { + GenericPromptDialog-->GenericPromptWindow.text = "Warning"; + GenericPromptDialog-->GenericPromptText.text = "There is already an existing LevelInfo in the scene."; + Canvas.pushDialog( GenericPromptDialog ); + return; + } + + OBObjectName.setValue( "theLevelInfo" ); + %this.objectClassName = "LevelInfo"; + %this.process(); +} + +function ObjectBuilderGui::buildTimeOfDay(%this) +{ + if ( %this.checkExists( "TimeOfDay" ) ) + { + GenericPromptDialog-->GenericPromptWindow.text = "Warning"; + GenericPromptDialog-->GenericPromptText.text = "There is already an existing TimeOfDay in the scene."; + Canvas.pushDialog( GenericPromptDialog ); + return; + } + + %this.objectClassName = "TimeOfDay"; + %this.process(); +} + +function ObjectBuilderGui::buildPlayerDropPoint(%this) +{ + %this.objectClassName = "SpawnSphere"; + %this.addField("dataBlock", "TypeDataBlock", "dataBlock", "MissionMarkerData SpawnSphereMarker"); + %this.addField("radius", "TypeFloat", "Radius", 1); + %this.addField("sphereWeight", "TypeFloat", "Sphere Weight", 1); + + %this.addField("spawnClass", "TypeString", "Spawn Class", "Player"); + %this.addField("spawnDatablock", "TypeDataBlock", "Spawn Data", "PlayerData DefaultPlayerData"); + + if( EWCreatorWindow.objectGroup.getID() == MissionGroup.getID() ) + { + if( !isObject("PlayerDropPoints") ) + MissionGroup.add( new SimGroup("PlayerDropPoints") ); + %this.objectGroup = "PlayerDropPoints"; + } + + %this.process(); +} + +function ObjectBuilderGui::buildObserverDropPoint(%this) +{ + %this.objectClassName = "SpawnSphere"; + %this.addField("dataBlock", "TypeDataBlock", "dataBlock", "MissionMarkerData SpawnSphereMarker"); + %this.addField("radius", "TypeFloat", "Radius", 1); + %this.addField("sphereWeight", "TypeFloat", "Sphere Weight", 1); + + %this.addField("spawnClass", "TypeString", "Spawn Class", "Camera"); + %this.addField("spawnDatablock", "TypeDataBlock", "Spawn Data", "CameraData Observer"); + + if( EWCreatorWindow.objectGroup.getID() == MissionGroup.getID() ) + { + if( !isObject("ObserverDropPoints") ) + MissionGroup.add( new SimGroup("ObserverDropPoints") ); + %this.objectGroup = "ObserverDropPoints"; + } + + %this.process(); +} + +//------------------------------------------------------------------------------ +// System +//------------------------------------------------------------------------------ +function ObjectBuilderGui::buildVolumetricFog(%this) +{ + // Change this if you want to default to another Folder + // Otherwise every time you want to add a Fog you will go this. + %defShape = "/art/environment/Fog_Cube.dts"; + %this.lastPath=getMainDotCsDir() @ %defShape; + OBObjectName.setValue( "" ); + %this.objectClassName = "VolumetricFog"; + %this.addField( "shapeName", "TypeFile", "Shape (Fog volume)", "", "*.dts;*.dae"); + %this.addField("Scale", "TypePoint3", "Scale", "1 1 1"); + %this.addField("FogColor", "TypeColorI", "FogColor", "200 200 200 255"); + %this.process(); +} +function ObjectBuilderGui::buildPhysicsEntity(%this) +{ + %this.objectClassName = "PhysicsEntity"; + %this.addField("dataBlock", "TypeDataBlock", "Data block", "PhysicsEntityData"); + %this.process(); +} + +//------------------------------------------------------------------------------ +// Functions to allow scripted/datablock objects to be instantiated +//------------------------------------------------------------------------------ + +function PhysicsEntityData::create(%data) +{ + %obj = new PhysicsEntity() { + dataBlock = %data; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function StaticShapeData::create(%data) +{ + %obj = new StaticShape() { + dataBlock = %data; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function MissionMarkerData::create(%block) +{ + switch$(%block) + { + case "WayPointMarker": + %obj = new WayPoint() { + dataBlock = %block; + parentGroup = EWCreatorWindow.objectGroup; + }; + return(%obj); + case "SpawnSphereMarker": + %obj = new SpawnSphere() { + datablock = %block; + parentGroup = EWCreatorWindow.objectGroup; + }; + return(%obj); + } + + return(-1); +} + +function ItemData::create(%data) +{ + %obj = new Item() + { + dataBlock = %data; + parentGroup = EWCreatorWindow.objectGroup; + static = true; + rotate = true; + }; + return %obj; +} + +function TurretShapeData::create(%block) +{ + %obj = new TurretShape() { + dataBlock = %block; + static = true; + respawn = true; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function AITurretShapeData::create(%block) +{ + %obj = new AITurretShape() { + dataBlock = %block; + static = true; + respawn = true; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function WheeledVehicleData::create(%block) +{ + %obj = new WheeledVehicle() { + dataBlock = %block; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function FlyingVehicleData::create(%block) +{ + %obj = new FlyingVehicle() + { + dataBlock = %block; + parentGroup = EWCreatorWindow.objectGroup; + }; + return(%obj); +} + +function HoverVehicleData::create(%block) +{ + %obj = new HoverVehicle() + { + dataBlock = %block; + parentGroup = EWCreatorWindow.objectGroup; + }; + return(%obj); +} + +function RigidShapeData::create(%data) +{ + %obj = new RigidShape() { + dataBlock = %data; + parentGroup = EWCreatorWindow.objectGroup; + }; + return %obj; +} + +function PhysicsShapeData::create( %datablock ) +{ + %obj = new PhysicsShape() + { + dataBlock = %datablock; + parentGroup = EWCreatorWindow.objectGroup; + + invulnerable = false; + damageRadius = 0; + areaImpulse = 0; + radiusDamage = 0; + minDamageAmount = 0; + }; + + return %obj; +} + +function ProximityMineData::create( %datablock ) +{ + %obj = new ProximityMine() + { + dataBlock = %dataBlock; + parentGroup = EWCreatorWindow.objectGroup; + static = true; // mines created by the editor are static, and armed immediately + }; + + return %obj; +} + +function RibbonData::create( %datablock ) +{ + %obj = new RibbonNode() + { + dataBlock = DefaultRibbonNodeData; + ribbon = %datablock; + }; + + return %obj; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/gui/profiles.ed.cs b/Templates/BaseGame/game/tools/worldEditor/gui/profiles.ed.cs new file mode 100644 index 000000000..ac11fade3 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/gui/profiles.ed.cs @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +singleton GuiControlProfile (EditorDefaultProfile) +{ + opaque = true; + category = "Editor"; +}; + +singleton GuiControlProfile (EditorToolButtonProfile) +{ + opaque = true; + border = 2; + category = "Editor"; +}; + +singleton GuiControlProfile (EditorTextProfile) +{ + fontType = "Arial Bold"; + fontColor = "0 0 0"; + autoSizeWidth = true; + autoSizeHeight = true; + category = "Editor"; +}; + +singleton GuiControlProfile (EditorTextProfileWhite) +{ + fontType = "Arial Bold"; + fontColor = "255 255 255"; + autoSizeWidth = true; + autoSizeHeight = true; + category = "Editor"; +}; + +singleton GuiControlProfile (WorldEditorProfile) +{ + canKeyFocus = true; + category = "Editor"; +}; + +singleton GuiControlProfile (EditorScrollProfile) +{ + opaque = true; + fillColor = "192 192 192 192"; + border = 3; + borderThickness = 2; + borderColor = "0 0 0"; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + category = "Editor"; +}; + +singleton GuiControlProfile (GuiEditorClassProfile) +{ + opaque = true; + fillColor = "232 232 232"; + border = true; + borderColor = "0 0 0"; + borderColorHL = "127 127 127"; + fontColor = "0 0 0"; + fontColorHL = "50 50 50"; + fixedExtent = true; + justify = "center"; + bitmap = "tools/gui/images/scrollBar"; + hasBitmapArray = true; + category = "Editor"; +}; + +singleton GuiControlProfile( EPainterBitmapProfile ) +{ + opaque = false; + border = false; + borderColor ="243 242 241"; + Color ="230 230 230"; + category = "Editor"; +}; + +singleton GuiControlProfile( EPainterBorderButtonProfile : ToolsGuiDefaultProfile ) +{ + border = true; + borderColor = "0 0 0"; + borderThickness = 2; + + fontColorHL = "255 0 0"; + fontColorSEL = "0 0 255"; + category = "Editor"; +}; + +singleton GuiControlProfile( EPainterDragDropProfile ) +{ + justify = "center"; + fontColor = "0 0 0"; + border = 0; + textOffset = "0 0"; + opaque = true; + fillColor = "221 221 221 150"; + category = "Editor"; +}; + +singleton GizmoProfile( GlobalGizmoProfile ) +{ + // This isnt a GuiControlProfile but fits in well here. + // Don't really have to initialize this now because that will be done later + // based on the saved editor prefs. + screenLength = 100; + category = "Editor"; +}; diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_3darrow.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_3darrow.png new file mode 100644 index 0000000000000000000000000000000000000000..1e06287e3dd67bbf725f9067c50d07b6a02ad54f GIT binary patch literal 604 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3HFyJAa%3QY`6?zK#qG5y$_yKVSk$CVK?= zGB8xBGB7kWGcf%852T+lFqG;sFuaOqV6d9bz#yKV`Q(T;0|Vo8PZ!6KiaALM3j`Qg z1dehjG#u!t|Gniuj~UzlMxQ_hG3F#jg=Z{DVhU+TmWu;rS3LfI`M*co491OO0^Ur9 z{EE&DOdLIWfBt`xKRdxdVH-n|rBfTws5`6?Jf4h6(qN8+Yn=tZ8AG??%N8C+4uu_z z2H_L3I2;%nCq7tv_W#6K$1X(%#+D?8P1+r%ED8+|*b<6a#him0Z%B2dHq4Ojn90b* zahRbbJi$;!s-sszfwNCmp&@{$LGqB{Vzwn8ga!69J=*P1!`D&H^wCE_k0D81A&jAt z6=YIF4nyLKXUiS_2&^h(7tm*aB<XO6>xg6k^OVLJ{6PP(Ey%RvJQCjULqR~F;gQt} zwF#G*8*T`8lr}~%b?gNCEsr7ObBMw{z9Z3%6;cAv88)&DNE<tRYS%ImJF?(hIxC05 z2F8H13YVi7{Cm^XwnJ7xn{lIoz-K4tdE8PJ3o=reI24|IdBRbe#du}swSe0d3(X9j zO${6$%g>nrG+nas&r>d51?7ww;WM4UFz8WPptzdhtFpP_ETEr(vd7rBPL+L?(gfxj zI_I|hoT_masIbrD8jFB~h;jqNA_+!F%xSnWFuE85?duYBU<mACVCqPMDq&_g`+!Z} Uj&JI3VDez_boFyt=akR{0O`ra*#H0l literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagleft.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagleft.png new file mode 100644 index 0000000000000000000000000000000000000000..93e2af4d102151b745c09c8bf6a6a183caccc5b4 GIT binary patch literal 842 zcmXAoZA?>V6vxkf;ob|<dZ}2iSjI!^OXE;lA`Jz?o(o+m%fvgTNk^91-Xe}>QHzoo z!iT3VjzShw$&hMDS9RICEO7-t_`xk9ilLi`F2iIlE+ge-j%17L_J!<)U9u17{6GBv zIp>#?oMENCRaaD21OU1=kE=sdv{?eD^|Sr`e`>;>ln=`Q-r2uf7wJti;ORIF(7y+O zL;=<}G+hPgI|#6L9H4$2V8`j6<(ZcO=qGJ1`RMrx)hGEt#9~+AMMd=|l^`Hn?x&_- zUpY8uzrig-HFEH(6Rj4gM%4ZB`#%E3x-V7CZw&dXE+64?o#-O1wjg7H>Igakk?H4m zH>x-M(yEYNIZ4j%6OZt@bI8G{_F%nc+e~m5@p$Q9O01xApJ5X}(~qmN_`X%!TgsDx zt$F8FiylJzL>?yhGrM)RHy@lL5NUvJXSO50l1Y>L>$_snuy<RmfF#B!1N}VFrSbdN z+Y@2`UdEc8Y%p()l@o80G8Db13}s}ywKL)A6EK8J7V;SV2Dvf(W%-Z=7k+DuZI=hq zO(D)RPn;verFSS=*y*G~4rcd*+8UB8zhkNKU5?(Dq$&wL-FDRd!X8iH&CoCO<Sy|q z8!k#BPRRs9>|Ae}nUc^6Hk}$}Regs^gUWj7*s+wiBvvb-P*Q)E?RAEi$y<+=KO$>O zDyl34ZBqD9BqElMKf`m6pzTbm?$Pv$kb3qK=jqwW%><RNTn*ac;w9G8w{VZxeW9L9 zE@I=2@dRGKg&W5ygKiDawTv*@sB-)D$*1uHt+NXMHl16?riU=>7~KRJG~FASSj_lS zSHCNcpA+Y?yA)Wlk|+6E>qpV4PUC`EnTSUIF)ZW{6vy-TcErXc&+CIgxjta<g<>s} zlRo_6Q~13Ry#sw>wpXD}Jj8AXE1+gQ?0HRTX1CX*2K3(quPL*{X8m4nx~-Bfd31}J zfO!zhUvsryL$J`Ad~(gJ+rPK)SWE94#Tu7rS-|lxu)JmDxME%ktEbWu&s-TR!Eh-j zCIT7%)aL;LWoK9sGZ%q=cUp6Fb+nM8T7f#WC?LX_DE2Y2{2%f`U1R%AxAAAQ_6*SW LYP;)ZvoG==e0CR= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagright.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_3ddiagright.png new file mode 100644 index 0000000000000000000000000000000000000000..59c09bcacbe87c1f05eac0a7f1bff8b889a4252c GIT binary patch literal 847 zcmX9+aZDR^6n?+!^?K5lT$8P>&51uH%x-gvj7>nh{OCG+mq2%G1_6eFU6}(zq+y#9 zQsD}9bY>E^5vXy|;%1zcESXCd{@Ak499cm`W59r!89OR5(}XyTv2oeF>L2fYFL__y z`(D0xN(%U`1+D@Bu!@2&s3Xn?%jo%7Z||Qv&_}Cls{tBrKlHCq`DRE6)&lgF04Re1 zxyL%L0i1XN;C>50)i{8ot0Sv!2cUiuebonh&daUNR^UkN%m=snuP3~E`8QY0T@BA& z%6)h9U01+zaMi5VlSx*MV2^aehZJ}=3%jv>-#fnS&_>uYx(?cLe2tMsyoRR{Am3?& zsw`R7*pbg1kNTsb<Q@B^V)D!ctu~W6-(-o-$e`rPCR+ALkwISRtXx2!M|<qtkl~7p zJ9bV3eMKx0O*odjB>@jn(xWo&MbO&uk02KXd0k>AZZp5}USr``o6i5LMB4nY*i37= zY!zT-#O9t_P9`?jgs$-JJ}SPR=26?zNc)iRNWFhCPf4?F&V}#+Y&Xd^eoU8tPV)b5 z_$}1hFau&NN+!Q1vI|&TN;N83*36G}8MVEV|9Q{?_$^v%!t;#$93DH0UvdD=-9ee* z^x<5!+hdGxC>3n{hO%Ue|Ex5Oqq9erpA_6Cpx9}Y$?-D}JS5_LGuOrl6RT+u!^t1^ z8{aQ+;y?BWMV2G(!nB+-;ytDl>Tx^|@*zf$Hg*#2qj40Z2M3Bqd&Gi11qNP$%C5mB zp@YiC>qs-BUY1%mygNcCWxKn!+h)lRBDaO%gjwB6I_F?!YiIk|nb-d^<h4$fmVf!J zK`Y0*55tiI@%1l)_w*aesJbo5hn7?``Q@JyyyqJ@)h{)EG7Ynzj&xHVI>P{pohFMN zRSPz3c9fDx;q2SV>qSQOq-E)X(wTRJ9i1Xpvn0g!?x62UjL`S8Nv>oB>SEIc!+)-9 z7rjqWsrto<2J5yhPt+M6cotKK6!5-=TcS-n_3g8FUzO&pn^%<-Y7FeUtII7LsJm5D zQCjnHN_UOn5&gYVtbi*_bHp$VRD0S+z*<4EN#qAqzQX=bazd8WGrQ&=>z@xG?hE)X Ids_$o1vI%PT>t<8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_3dleftright.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_3dleftright.png new file mode 100644 index 0000000000000000000000000000000000000000..63c20e16abfc451a7b50d415b3744e3c8445ef03 GIT binary patch literal 607 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!3HGD8EPYe6id3JuOkCP#PL7w514>_g=CK) zUj~LMRR)HJW(J0z|AF)~28L2S28LJh3=CG&85qR#GoKvMW?*1^<>}%WQZXkfK_Y=c zz#&2~y6{eY)(S;71{MLMB$gzx2?v{=hd1(sGbE)dOk+rrn;;Xy8zJk#dqwb6z}6&( zGrddtG=LgUaOC}L{U7hZwwqz2w!mw~jTQpMOoqlH#Z6ZfESZ*E<LF4eAmc3nG*w11 zny=JBP~bVkMs<N~ri~f`pPe>p_{dIDPHMQp)$!D$ki~%^ajQ&=W6}?UO;IZDOB`es zS$PfBoXVJ41SA@HUi|nk!kR36xP9TvG$w)LMh=U7IXWUaTbUdf7PdG%Y5!c{qro!a zs#5C#H-&X<N$v{M*pm31_!u}8c-#-jFo{$#9{JwzLqy<xlhi)GF7rb=Zkztt3A|^0 zWX9&PYhtq-a~wO!^F=OYELRR_2b`YH_%TOe9^cW;8zbtb&Jh5blfu%qJz`1G0}X+E z=0}}86J#cG9Jv@Urz7R3*@|0lc~X`zHZVB3EttaXbx<>)`8o5)6oqpvN#+V>DGe#^ z0%k3%PPVLKT=_OpL0KGR6VuaJ%N2}<JS?XeSIkfk@fBpA`INzdK~Y(3g<C)-o7UA2 z(^>)yZC6Nb@^%6l+!N5d;FC<qI&FgoE4ddW3Gs+rV`X4$IV-3jzsXtm0dtZbn@5sU qpTF7)3GonK{t$lSmD8A6Bp6s*Rlny53-SX~2!p4qpUXO@geCxTNydKw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_3dupdown.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_3dupdown.png new file mode 100644 index 0000000000000000000000000000000000000000..c0897e896ee1568904298fff491b943ea52b9a92 GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3HFyJAa%3QY`6?zK#qG5y$_yKVSk$CVK?= zGB8xBGB7kWGcf%852T+lFqG;sFuaOqV6d9bz#yKV`Q(T;0|OJIr;B4q#hj#s1p*8# z0%1qOWWL9L__ON2!mJ5V4Gd0hJq~A>F5mh1UvraUpTipVBS8*ln1k3u&oc-(oZwhz z`RQ=w21fCJ^^RsLVjXK6BKU3!G%y@0Fx$nL(YC~acOqj_o<bTMhr*g!7kM3wRoFUu z8zOi*ZZa})+~nqRc*AtWHKXwdQ^$MekAVvF_$RfmVqW>2kwYPcc}n9A(T-vVDM!5q zsRI5Eeddp53iEg%3Yb9(R2C?{?MyqMsc?_=h;XBYunK=Dd(e4SptIN-oWf)}OfT{< zWX5ro?vnP9TgA6zo~S}Yz#_p8%f<+ivkkotHryS;jW+}!x>!KE44f9X@}F%eb+F;> zSiErCXUD6J!a@!VfjJC7;tgv91Ji32L&b$GN)8MM*Qy(7Cc7v*Fep|#&U8pSG;0C_ z&_@ic3S|c;&Mj0+1%{j%!xT%0nJR3$`VA)WtdHJ0+&SDZZ6;V*z&DoE;D9pL)Z3gF zByMmoRX0dDd5(dxMXq<jgsIJ1Ori=`9qzC#-6*C36wF~zo}?IbVFH_IsvJ*6mkH2l zcLQe!PFIeOr3=lxoPW-FF$)-+(-nPqCb%48yn2N3sqw;WV30epEllPCMw5tg1H&Q- yMizmi910B@ZVZerhD;m^U4lSg^)N8;Fen^V{c?K0%z0qKVeoYIb6Mw<&;$SoDbF?l literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_grab.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_grab.png new file mode 100644 index 0000000000000000000000000000000000000000..7bab05a388d05759d348183ab2039abd5d5fa632 GIT binary patch literal 769 zcmYk4e@GKy7{{Nx?Y7f)d7IYUX7-wv6Vp~vm?gZEl{1=&WSB|WX{8ya{UW5`y=~b! z_lM2ORYsPDR!~_OqoAy=)Eq=1p%qc1G7KV$ilSK8fBo@1pXcEV&-Z!YX(=u$;9||O z0KgSG9VH5lN?2Hpk~=;myilOtYIoTI$_9TIyrz{NRXIys08O(2hzH<jRN;GoJ=p-0 z8v$}Vlo>l}-^!T)w7<|{U$d`MsO(As7M++y;A{r{Ur{0HeaE|kdM=&;nzabR)%iB9 zkcm#l`^Lo}C#@5MjC8!EPmPGJB7En-^XO3AMATD*HkxZ%(k1TQ6siySUG^;Z&)7`G zA;DwMrcL|bM2*s>#hPK;2u`$K(aMu}E+hZOVezpMC~tkv(r`miM0*FLJ+_f;ZTrv1 z$^s5^-<W1bk^Okn@RWu87Him0J<@f%K_whO$GqhO{(6K#)CcO)o^Xfy4rh^zSrEbK z86)U5PH%T88Ib|@liA|2Zg1|sWi~r5k@Bx(ELqx|h%WeI#w!Tq&rcfXtNrykfsYn( z%qW$Tpf;~Yxx7B-X3E&T8Le;r;3P)Q)wQKPa&OQ6XXi}0#&~syG$XZL&uVd}T3&(Y z>RgK&fXCM9XuW&h1K@6Rqt`4_I@P$0w^S}nLZt2R627N<J-Pq64)<F~$aF%$rq-Ex zVTxu=8LGZae&V&S_*`cbD;aILq;-a>H#iU|R~9kFEWkTYrRtk^Fp(IXbRUMD`MR>5 z85mMhi-)zo#^vY+!|3_dOeCwg!~HoKy<+8EV#pxniDfNR!2&gls%3Tmskn=GP11D| zI!YND+r?0v^n!$rPyw}zl+lwmKcZN)TW0$)Y@>Qt{h%WVP1QsIeb)%-8t0dGM%bWn z{(8t72;6(8THG6l4+dyen$~TzKAm=^*=P5t)?TiHe`?d5-a7n>Db>AnCdw1uT*92b q3zTEX@U|(z6kmFpR`V`BCKOy*Ia=A1{r#x&!%(=q$Z<Kp()|}Ej|sB? literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_hand.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_hand.png new file mode 100644 index 0000000000000000000000000000000000000000..5e107364462c1d1d34f57e5a6448bd0208d7e6bc GIT binary patch literal 884 zcmV-)1B?8LP)<h;3K|Lk000e1NJLTq000*N000{Z1^@s6m*^DR00004XF*Lt003CW z{72vd0000WV@Og>004R=004l4008;_004mI004C^008P=0026d000+nY2?H!00092 zNkl<Zc${KjpdkPVAWURqV8XwB+JBB2HUB|uN{j*sAY{Y{G|U`i=!O4Zz!+=+1A{RT zs{yeX5OV@CGgvJV03d*n5fjjGD=dZw0I@9)D+93r5VI0%I6wd)BW9rCwpa|$2jWm5 zHUeUCAZ7<Uga`l-K*)#%Xt+HV!`py39qM`+Am$?0aDV_pMyx=?9kCcb8HkI3*bRu4 zfS8W~5;PzS7#Z*c5kLSTBQ~JnPUwb1Q_XZBE&;0117Zmv<^^I7s3oun#;^n+fDi~6 znLzA{Zg}K}^I(i*Q3MeCGC)!fKQx$-ErA1o0D>880@4Ue-!Q{Le3;?LY^WtMKpY6v zEeW-R1Kk3E0D>D1(~N95%rJC}td;?i4-|n|kO7jC;1&P`5ZrK(Vpvch8;;K+sNtYw zY6-;hNRbUT93X%&3<tRim*MCZK@BehVo>nt0<j3x^<cvR0*IjD9si$#F*+N`@ID|; z24V|n_=AcFa6SMCAPmEiT?jLbfZ?-&I2VZR7|=2qKmfrJvY{jzz5s{|f!G1vaDV`U zBUlQ+WdQ-h3$Pgu5J1R56N1eGbOVu-BT&y7Xt3mfOaNjj<S+*aAY>#DRGf}(0WQO# zuHFvB6M;Ahh)o$Fr6va~wEzSVG7@C~l^r$)<;W=po8eGbUjV9K3B-*+91O(TKrF-n zDx;u=0|XE<;sIh=pr%AZhC^Mwi2+h#W&^P!5Gyc13N#j&;Q#@Ij6gPmY8qu|<fH;| z7Z5LpdVe<%?*!t_K)eEorvh;~10)0KL4D4F9Nqu{gp8P=lEOf&4#dttoCw5KK->w$ zy+GUp#O**_2aUuKAT|U07~*pjR|5nPG6D&L3LQ{vgR-$I10>gZ0&x%nqznm#vb~|c zGXP>a21u#MhVE*B0K#Sg3)BKoDF;egN>HC@0<i{^tpYU^RHpMH8I0m;fB?eh12(87 zAn)=)UB?e)gA4>^7FcbB)nI@C!e;>^R1-7QH!#eMgz*{*5MTf@?0kCghA(XZ0000< KMNUMnLSTZMA~v-E literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/CUR_rotate.png b/Templates/BaseGame/game/tools/worldEditor/images/CUR_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..24c91b854fa711210b43b97e49861222d3250491 GIT binary patch literal 1145 zcmWkudrVVj6hHUg_O`aAy|9ke+Rl$x#c5bAis0P#wG>*4n2P8MF#`(fWQdO0MI9{n zwjy_6X6G1VgxNZ~WD?yhZti8aA|>L)k(sF_F={H3jX1S4IvGTF`Qw~la$fo6yxw=N zwI#{4G5|;_v0KXpTqp>V(t@7y9yJRf+G;Kn&~<Lsc9WcQ*4fL;0QP19us(q4rvgp_ zcyfe{YJgRr06e#2+eCDUkSZ#%nk#nqxoaZgM#Ym`U4>-h@LSenLo|@mMe&|>4+4G) zr#{#bC_a4~h<zVuxSnVEY4iT{0z~rLITyiC<0fCF;<PSAf1V@aFS0Hz{{z<}ZZK>F z29NObC+7RpP-!ilQGk-Cps2p{!)425>3&_rH%15*@f+Rgb(xY-e0}=__zO?4uG9bp zB(LuL&ivP|#v{JtlKvd=6f8!QBDZ1Jn*=F6u<FUHZ42O(;$nOTx0wDJ+N1eJIZmLw z<_dO{>i-m?VSHQ_wJ7}G?2Et4f+egwKd|0@$A(*;3DNZhhsL_k>R=<~VQ)@vFkV>6 z>CZ=Ql2H>L<(djGaKE=bt&V7e<CDNt`>J*l{1`id8*sNzVQY>M*{w?cMJ|R`I4`J( zL?%*_?#*b@qT0)XcSg4*fBE1hGM<6YBpccr3$782{2fTp=ymxedAaJ_StB!6&xYPx zLEEnshINRINb9plNq_N|)M-fULQN9!!lf~DYN1|oc81$Mzj<dyrBXIHj&<UwR^hKW z=$<2(JUTq0#Im+3lw&?Mt7QW1l`?sQrnuvkGu@Pk(G8X_9wm&nh;iU(jalU$GOp3v zbyI&H#jEAjCxw8Hd*K4mXS^hx)M$zaG-{KC+nvrx;*OodKn|>7^QrOVn9h6A5<2uZ ztqzd!%Xm{#^fyKRfEGhqMizPm@n&>b75%AJ$z|CmYHS1d4NSE--iceZ{J&%(1vQaz zBf7FVvTg)Kj47p_%~bd)lLj4Y$zi^h#Ho|V>9RHV*vDDSjaqghK{-9>p&s`oMSoWK zFO~HXwuEeMd-PM_l=hI}mCPbs=uYXqeUN*cM`f-%QOl|gx1A4BYp|+J%ohbybL$?g zm?P)E5YwPaOBYe)i7%-S4qpEij#;m<F$k&p1Coa4*LDgUq+e!=OnLY%^ZeqI`R7t@ z$hq%Ho?>09&|!6{;16Wrc_MXEuTc{5>KhV!XuJPvE*|*EQ1SBheQgTiHWs?~|3|71 z+%-zj7x#1V884A?^{&&i+SzI)SUB5`w?UV5n2l-px+AGOwy+i1=*M=VeCvY3%i&zc zet85Cdq2%J>+2|TrV$Mvv%R*UC{OGd>Mb=nJ(<I~7?N*+b4IT23qI0h%ej=-6kU0B zsQ<J5dZD9>l`yU7>fOM(B#u&}KV~hafv|Q^*erV?u!nPj<87mp`9rYvQ$4}%Fy3Zx tETyH$p|dOlX^<!aVtG)CSXxbpG$3`{X_a|PwhO-lN{ZH6zp~VL{|ELdmqh>o literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/DefaultHandle.png b/Templates/BaseGame/game/tools/worldEditor/images/DefaultHandle.png new file mode 100644 index 0000000000000000000000000000000000000000..c32ed3fb8529f575734eb1d28b5b08eb2d874a53 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSpnVqirF9h-xl0AZa z85pWm85kOx85q8(Ffg>7XJE*UXJB}d#lT<#)FU0f@@=CSP_3P(i(^Q|oTMNB|JyU$ z@d!AqVB+BkP;8NKU`X^@q{+mg@Me-n7Xwqr0;UEAr3_XF2Eh^)2Zo1Vl{lDLBpABQ WmPa1?y?ZmzBnD4cKbLh*2~7Yznl3p2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/LockedHandle.png b/Templates/BaseGame/game/tools/worldEditor/images/LockedHandle.png new file mode 100644 index 0000000000000000000000000000000000000000..37f2ddec4884dca99049c6f00b228a919219fe94 GIT binary patch literal 340 zcmV-a0jvIrP)<h;3K|Lk000e1NJLTq000UA000aK1^@s6TWc(!0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_DMuRRCwBKP%&<UKoH!V3<V8^iHkI8 zkgSs|en3IKK$Xsq@H6risMEs>P^8F`8!C!WKtl>5$j)l9K=MeVxt*O`mU|*amSq*o zk!hF{^O~k<gx);QVj^^7vCLoQx2jcNuv2|yc0A#E^@Sh!tS8nl!-o&;ebN?7eO@NM ztMR6LF4(K0f7$uIe@K!97hZU2kfv#p9{|d-Jjg`Xb;%g>H;yA!RYk)vkmq?6MG-lU zL+Fz}g#e+tuH_6xy~aI=NxTNdaV(1ZVK4{+>ieGZJQqd1#`2D2t(BeHwv|vAhTCA* mbyO6^#%!8K2%r!A3orm*(PtUx)S$lr0000<MNUMnLSTZf=8wVv literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/SelectHandle.png b/Templates/BaseGame/game/tools/worldEditor/images/SelectHandle.png new file mode 100644 index 0000000000000000000000000000000000000000..941fcd9c861066987aa664754ab6039325a5f80c GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSpnVqirF9h-xl0AZa z85pWm85kOx85q8(Ffg>7XJE*UXJB}d#lT<#)FU0f@@=CSP_3P(i(^Q|oTMNB|JyU$ z@d!9*$fzoC9y*c4z}TYLxsF4jVS|^FHWP<J6Bh$h#{y9XMwbf{fpXWo6?g;^7+85$ Vb)JwHd=4~;!PC{xWt~$(697HeEmi;k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8cb9baff58d2784e4dfdad593d94475130c14b8b GIT binary patch literal 429 zcmV;e0aE^nP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzPf0{URCwC#mQ7B>Fc5{G<DZZPl_D-+ zsVphtAZ&64Zovt-L6%%08+JwX4xAvXvY`n-Qd5;A!eg}(1*)V`omHM>MT%m7GmbrP zSRBV!01`7a9EdRn&N+k-P*SB*$w&||@Rc5q&k=?pJkNtGW!XZ%W_dzT3%r<2a5@^< zh+L^GOFT}e+NMPrx(1SHeJLd@l(y70a4Y5e@X)iQNv?M&qg`$K=<@8kGvImg@c+MU zGx<KbI~aHq-J&Q~oAbi$UERv*_AZMhidA8*O11OeDRjO5LRD3$>$*SaJ`K<TIzR_# z_kvHw`>0UPy+(T?x1i!pwe&b`3vW)A?1`*Nslx#@?{|kOOdr}NG6W~VAGc_AXf}Sj zv!1W~G=IxAwTJtfGm7@733t`Yd`^$lWU8X~kI!T_d(u)W(}hcL8yRzPa8om)Zvh4X Xg7Hv1iZd&b00000NkvXXu0mjfo>jGu literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b380cf4a624a7f738a112d0ca5e37d07c4da050e GIT binary patch literal 611 zcmV-p0-XJcP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!1xZ9fRCwCFmcdRVF${*COgg1hiw3dM z@K#(a9)SCvIHF2i_Ojf0fECw$5_k(33DF*=Du}U-ourVWE~~N=ri!Xoiof~oKX%N` z_4OeDOk`D%|C!6Watf^Ly82$%*@^c}Q#DO9al};Z9~=N<mmR8{+q>J!F#{kX@E7Ek zWGPRe3`7WEY<?&cD9<qw5DMN>ekd_0&yhk!DQ#dLADx_i7V!Je<#aPxpu!fV_}9%p zFQ!`qPk)?2+qQ+ZmYs9#isIEW%JWK5mgzZ_4-d0)Fc{F@-nVCK3<qFTs(Hq)17rHd zw(Y`=c1PXL)6j_0pwgSx8;P?h4NfKhjj?>HRC&@I&^wBIcWQtIqf+uR7?gHMU`_u} zjh#Z}XHbfyN)fPUr5p{aYB4z-$_qxyQq#}~D5`yPuNp~<t#8t^dsk`I#)GSx>WeUi znkpT7wi3TWRTL%v^IIE)4DJnCT#p}`gFj200V-a>V5T%a`Sdccoul5;tDjiIG`=5g zt3&)z^Zt}lZ442Z#t%~_&bUp_`LEOI^6{xyEEeT*`RKNvogu|({MZKh!<t%axF`x5 z4$G}CQMym}uh&$d@Gj@?ADKQp8tpunWofpDTGst*@i~sRe;A2%7|Z&v<CCUc>ok`s xUyaA(>~|#bhQDyDd#~oR*+kZt-SA%o7ywsk#R^R(od5s;002ovPDHLkV1fth8Rh^0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/boxBrush_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..fee5a9cf36d21749d8b1bff7e0984b4b50996a15 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^vOp}&!3HE7PPGUDDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MKe5I978H@CH?vT-=29dqwrydhCuI3u80E96&e~rR~K3H zzZ2dj!s4@b%}Kvo%ll<hRc<afxNz&1)27y|S4|bVWIO&devH@foo?}zF-cD$P3`7# z5r(oDIq9>@7f<VpvdVfXu;SJQuKZt-Tx;juSlDWAeYb{d?X-UrGXKec$?A>pcKrI# u+~~1<*!)RPFV|n`2|gxx`34(@0fUKvqFQ*p<O!e~89ZJ6T-G@yGywpm?p`?n literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..056bae49e237cf1a2a08fb6d59b825ac43bb08ca GIT binary patch literal 755 zcmV<P0u23$P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!l}SWFRCwC7mS1SnQ546&&VNBD8v{XH z^rMG#2_=EdmtHdLp@-?l9vyoN9B#dYl2HT`6nbdNe2kb6HE8KmK||ldH6eOvp%G=7 zIuw%mtH@~CxmOfMYyQa&T+Z)a@4cUUzW4loM@b|SF#(8C6EGkfl1Lzv%b`#xpj0Yy zO63uUj6pOSjbJnyp;D<Jp-F?ZN~I#~?37{*#36zp5M4QNkXEZjwOSR1ScgUOKOoH5 zo=ientJ5tcolbMVmEPh9oORB2TqBiAo1kuOHM)vmFo>24q9`Jp%|fr&cLWag0!k*6 za5|m**=n_NpGYJ)Jd?=`4#Z5YQgbiXMD7g+L$fc6hpy{Nx|PTZm#A1Q@_r~3;=r0% zYmd1X+*p6O4xLWdUqnn2da1cD6ben*&RbzNn-LC$VbB}6oGhZlqUs1@CuA4Tn%!>S z_Zu6Vv`-=$jl$#cv}NoI#5UV_y1`G&B5EZ=>t1|5AJ4dfWF3R~m;C%4N+c8Q@jx(u zM!gP~%hhp@u99J|R>R1RC0zRPi$@TT$9XjD-sZpWVfU6Fs@s8o2feyHk8dQ~&G;kC zeEq_|3e3D7FOGl6BXiY@pJO@*SKR%B-rl{3_nXgg?tT<UPmRM&V=Uv^M$ex=74UL7 zjA7da4AY#R@2F0ar6`3*XQnV}nfU8G14SY+j^i2KV_(VehO(_hurmwpj`E(pehu3c z`A2F!gC5^m#))YM)ML7ipkoyA$b=bBXw88jIhEXlESuWIyL=v}r>DC94w~oY@FJB$ ze*5*GASz0^QmKSSqp?4wso546anfSx8f3a?<7-5cyJh8anYo-~ZS8@H{yfB}!V0Vs l@@goP4YJzis5yQMFaY=Y>xp;yk-`7~002ovPDHLkV1n9uUGe|` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..8b8f181e2239200aba81a4ffab9a72cdb054f786 GIT binary patch literal 877 zcmV-z1CsoSP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#4@pEpRCwC7R$oXOaUB0CA_tY7)z&@C zc@m>*vxuq9o*Wj43}!D|j^POQ(1T#@x#+2f-eR>q2(d8+BL*>h^r6%m%%d4i1``I( z{;0QfcG`pH)b{(`c2S~t?ZGd6_}+2%{eAB9{eFMn`$@L8wg>=#9Rdf9F&1K3R;g6% zYPA~g=;&w&OFEs#cXxN~($mvZysfPbK;IDjTN_7;0D?p!5&gQAl@$_snx@I-=H|l8 z%uG-$29crlIihm8EJ`#xJ8SfKJQj4_($WHskB>n#8tv3*G(V@Or-NcdQbckYghHWi zhr_`I4-E}*9uoB<^0l=!LM&d&D-r`Tg+jr3qy`y9QJl|YGMG^8rBVm$aCd)yzmsKI zuKgDl7C6FOE(a2c1ozJT{QM|Ml5VY5%hpX@sZ;{>;Pd$i2va_vr=SX)Pn*GD0E>%@ zoFbIYVzG#&QYlPmEm;MsK^L_Yb#-;Ia=E+!i;s<rj9hqz8p39?0hh}KtX8XcaBz@8 z>(o?)YE%}eQG0uPVQFdU`N6>f8;{2U-wb@6rlzKVUat>MOiTm?Ys`0@kYu>w3S(nq zt8i2S*j;Btb8~Z`SSXa7PG>+U9szO4_?vpoaB{?AS3Z8S+-X&E3Sc{PY9f&cxPO(9 zF>l{llrHD0&_4QGBPdIVX#2&>(IRYwr_Uzc{mBFt4u?slQVGV#$7456ium5T-&T6{ zI@|Na;TN0?d>(5Ng_qwO50gn!`|!b=)*gy6_fWuWHpkAtlQ~S`(a#~1qPI6f^z|~e zh`0^Q6IJImmri5fY1$-Lsdfm1K2|UPw;n(8mztWG&<BsDUVSbQRpaZrk2=cdvF>}; zfY{W~44b`4sQlaJCi&kZXnSMB)TFw$qg1I_QKEr?m{i#sqrF~Jy%AJX(Y8N8{>o-? zweI$p%MM+&PDjV%2{IB2)f}*Av+lh8hWFR|`T{qN##r^UdZrs5j_m!21+r`wtB&FR zcW$?Pk{}2iT4N3jEDIQhVP&xWmSG|75Dh(?{1IRPDUqa_NPbqZ00000NkvXXu0mjf D@35J$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_n.png b/Templates/BaseGame/game/tools/worldEditor/images/brushAdjustHeight_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d0d03edb579c815d444041512fa5cf774f2769d3 GIT binary patch literal 780 zcmV+n1M~ceP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!u1Q2eRCwCFRZVLXQ51bMHHir(Rt%C5 zLTXoq1gb&^R>4TjI{5<yH}1OVQtTpCK`M%sWZ6xzJCUxl3C52_ldOaU144;P+m%`q z@-a;&@!a92OiMnC<iX|6dvo7A@1A?^jMV9L#3Y6+CNw4nbZ*eg+1c3>bF<kT<VW&{ z^?LpFp#8hMyQ2Y}0$GMzr)R6xDk_x<t<7WlWf<VN)I}B-7qy2_C?pv0K7j-9`hTsF z&1Ta)WkX|bZcg)1?qxnpI#Z39vQ;>uXolwTcw7T4m&;;*e_xA{B<Y>WWV)#+$~R0~ zV`l`AYj81_%N6Yc5p90IUnCL<EhDD$BJ2uZTsjMNB~lj^KN^jmctLBCNJMOHZ4q^= z$mS~p$zuaSO^0w-i^byiy}iBe?T_oUva&)T8;wR|14~mdL%*!#e*M$!A%OkGVGnY- zoJgfo$1!VbYoc1MDy>%Qm2Rul?;NSuw@%-vpy!goV0d3B6y{5%l2#we23ZQP*DLIH z``ctPxy&M{!y2arL5({~!)`a5O^@5{4q$^Lo6X)KCfe=xj?rjztE&1U7!3YQr_)cd zuE5k@hYZXp91br-y6dtm-^HyzW9b6oNnxfgO@TmQ$zriAfyP^)cLz)DUZ0}AVpv#M zm`0IfuuXt`;9|>s<0HPklF4LVLU9FT_nc$KlQ%X;A%tnz6IK&t(Lsh~*e}>`wm+!Z zL&V?q`F!^nY;+(1#9*7C;|Hcjj-^bhr!Ak)Cu_CZCn)d{UrHVc$m8(@0Wt%7!+kNO zqfF&GsColXoU(E{oi}=A)l+pxM|7Ted~k5^5&k^$-s<-qBT6CtZHL403@V)FtW>=) zhV>CqX66m`0YKeh;CR9D&7rE}Ea-iQ0oo(<oT|IbiQmRQ0R{lHAT`^;VDN<i0000< KMNUMnLSTY(+Gv6R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_d.png b/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_d.png new file mode 100644 index 0000000000000000000000000000000000000000..66a10bc37d3e10a1591fc0e3cf7e4b9db9975da4 GIT binary patch literal 791 zcmV+y1L*vTP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!xk*GpRCwC7m0w8HQ546&+kV?`WLxy0 zrrYQj*&jEkzz2<<g0!G0mOd0|>5J8a6a*Pzfgyz$gduw>^sNU`LFi>BEqib?l9V<D zAvXPutu2~Ln@iDO=gyoiH`#0+xZK~l=l;%zd(OG{$SfAi2>^XG1Y}9%G8s6f5-ODn zJg;)|yxPn$NM2r^U^1DY(P$u}P1%%SFev!_zCH$uK?Fe{I-hk=K|ujRp^zYF3OmV8 zK$tL}EQInkWhWqOqEIMSLFvfsxr<nM)s@s{n+Reyvb1%9qzekWCJ=FqAl%)%$#%MB z5MMl@E&u1zf&3;&m_i%2zQbOF9yyyfM1hr+{|7D2&lBixatWGC^M`Todcvboj7@!q zY<W4R@6Xpj$k%2L3RnlRtk;3p>*2RMU=Y1Hd#^igS4lu)$9p2K>P<CeXu4jO=!9Md z$?Nq>sbq0(P8v8?B8lBSF@5Apwe-3EcHBPabV=G%&!mr|o|Hc9cDuA?G1#uwz}en` zC9MX!s>`vp!;c=~8|5lYr}ENlJ%0&2g>JmL1&b8ojH~V^<zm|5z<6gDgt{B?^*zR7 zs7=L~>gmPM{RcR7w*iXC0z%Usd}?``IVj2s021W-!kF`ol4CrI;r5r{$aefr1AMMl z)LEY3hbU$aV)gTjM)i;lyQv5^ib|)8*@|mx6ZU&tuI+)CLnd0iF2Q!Nyc`ipLQ%z; z_*yQVdV#91`9Klg+`N@OXvXD)T349VwvU`%XlsR9yM9;LcjMS7>fiQba@dZqR%?2o zf!Ccdl$NFpMM+g<^0r_!(n%}O|Drv;3w!N(8w@7nwqEPX$4!`VCgzJet}^N7BPYwY z-4=FH82|lWPEE<}^Z7s*ct?6sAP``an^UWKvnYyvthcjH$Ogx8*=&Sv8s<L&3;<X9 VB`|x8-XQ=0002ovPDHLkV1n2^c3A)b literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_h.png b/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_h.png new file mode 100644 index 0000000000000000000000000000000000000000..921e03bc74d5abab8e312395129b468a37447ff9 GIT binary patch literal 895 zcmV-_1AzRAP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#AxT6*RCwBAJbn7K1Oo#@1P}`_&<1<} z;uS3a{{4%PmzNh15fPz@EpOhu5qS9UVFc6v|NjMqgoGG?G)*wnYypsu7(kejk&z}o z0$Bz!p9w_#{reX$K9Z50er?7q3v6<0_MKCB`szIgKK)>e2w4KcU*Elxcz5Tn{LRHn z4bbIU=WmNYdhw0~Av0KDKEf4rFVCEn_yJV&<G}+B20q>oAhpQg-|t@xfB*bpKo>_3 zI)WB``}mOqWDy9wfBpjO@{Rk?D!>f_T6Fc+Ly3QXeu4SQvf>|fRAfG2So8<z8W6^1 z(Ss8w)&FvEd|>0@`SAAHGlf?-ZVGhI*&Gj(XZp{;ux0NV_bvM%d<Rp_gAF;s2aqiS zna}bM=t5i;eS7zg<H@ya2C6R3J9WcCFYK(Wjz2kXzWbiJ4UHiACuh$|Bv0I$UXbX$ z(9_ZA0ZbprJQN>+;^;4qpxabY70)Xs`r;2W^AD}ypbH>1>VW|}4^Nqz{^HgxftS~B zNL*UGE}Z3$Jj1mWOWm!tWfyV_3;n<rM}J|d4i<bceDBn0iMP+6D?GS($wJJ~;3Q03 zQeW={6CdA;OWSrBoZYa|U&7evAj8jJ4BtP064*Cwx;wf#Fbqz0*y8B(hxZ)cfs*Xp zTp##kWnLf$1*?$Yi<>7;+H(mAys%11K46u?aNy>lLkd@R?y^T#hZ#peZ=qZC3up<* zqPX6kiLdV66F@eAnV07yNKW0<_#{jmr2h5m*Aloa!kJ-z{{iL+4t8*!5tf(#fGj6( zWOU;vkk+uXy8#oE($IME^Y1@~Nl7X7OX}*wv6%_C2sun2-?<~f08G-@!dp&P_l35L z(?$+Hz8^3#5Ibekgie>V)R`}zJykfiX|o1${GeEbjIW$NWzH`p^$=HaVi^!{0h_pj zp6&~NX_*@*_w2F9Sp*>iEB}CvN;=vva1|@~fM-(T&U;s`N+9b6mkcl)k%6wY6*%*e zV-Yt${|}IUWW6|jL~Q^oR6o3X_f7x^XwzW$@Bw7e2NqyDU-A6;^9UfOY2ySSzyQE} VRY%^+*$Dsu002ovPDHLkV1gu9o6Z0L literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_n.png b/Templates/BaseGame/game/tools/worldEditor/images/brushPaintNoise_n.png new file mode 100644 index 0000000000000000000000000000000000000000..7d14ea43d392fd9bb6f6dcc8f451d4ee7cac07ae GIT binary patch literal 791 zcmV+y1L*vTP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!xk*GpRCwClR!xgiQ5g1mZAy~RRV$N( z(nX?$2oa34kZD}yT7oJ20Y#hI6a~d>hNxAGV8C6}qRrsOXvl?-=!{N*BG~AN$jmT< z!hj$5tLF^wg_B&znF(gmg@<#`^Pc-Y=lwWG1x?dTr}3Cgr}X@PLKY*^?RNh)Pb3m+ z7?Mj%OK(Tl27|#F48?pt-}&c=6h#RD;raRbd!sg0Rabz(i6Y{kcsw4EB#FJofCeBk z|G+`J%Cf946=>8>Bm@z={*I$?1H)Ob*LzbC1QpN_DdVquJRS?1!~VNcscaluXgD0+ zfrC1nZDKkd3Wcuh@9%%*#M$sh6h)nS3A{L#(A?ad9TBm*JWi!jAEVJ|+Tn04<Z`(S z;Ci#Ov%Y4txr(T6SF6<@ER#UrD2-!<B9X`n@?S(Wopd^#1a)qEd;1xLzWM$B%XYin zgC_XI)>^IBMVHGJYcv{rdXJE(tcw|TT{s*LKx77ZgoxhJI<ZhFlraBgv)SU9e?VFn zs79BZPUmZC6br%3MP@9-8URjMed_&w|0k$3^bx4x8XP~txS7dhULj@YQJ=dIbh4Ac z)&K)Onz2G8V#;Q-SG(PAn{}FQ!X`GDt=H?lCvgEB!!??-v`HkYO^S%2&Qeh>m$xVt z%z*;_3$yR+>?~tG#jv}(yUsY&ZqMiQeKzuIC^Q%h0@!tfvnzTkN~KZ}wg;_Nt4lS} z+U@oh_={-!Td3<a&Q$D3LfG9yIQ!0t3@9B3sX&xT?skyLHmucZtJvf+zJE?x5Dsl` z?CL&>yFL<)!e4R32_&NGe5ccSigx$2<G?qCVDt-1x3@@(&2$K*k!2!5;Wl9=Zi<4C z=v(!$n?c|rQVr~?0k85GF8MG|5>mOe7zrKrXG1?vdb^vBA~eaVY0M^M{@XtS3;?wl Ve)n_DwB7&!002ovPDHLkV1k<JbhH2f literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion.png b/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion.png new file mode 100644 index 0000000000000000000000000000000000000000..339ad47e8e22e0222ce883ac87a7c1ae6bdd3624 GIT binary patch literal 841 zcmV-P1GfB$P)<h;3K|Lk000e1NJLTq002M$002M;0ssI2B@5<>00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeWspsM)0008qNkl<ZXhZE- zEw3Um6n$$@7!U-7LXnW5_yH&c5(NSS3b8?<NJt18g+ifFC=wC`$^MHc-%r^+FDKi~ zcKZ4;oz6Tqoe)xnJMH<n_qOn3yWPIM^aTRHUiSJ_KN2X@zUV-i>VK{s%7M4-Z@da% z7lZ)O7sDYEP-RJUz@c_x3dIz1NEcOTOre-UFOx!nHs;)FAc-%%k%imZ@*yDM(|pYR zgqJ^oBzoo8GwG$rBZ0qy7={1tMGPW(4*6z|m@S_a64QOfArSg}eo~%Jr^DfJy<Ukj z%>g%`&*x|~I-k$EGx2g@x7#(FP5!A@sZ`eMwSvRXk9NB){qfi9^%jf8?RN9$laT<l z>~uPW1rA~iO8=zO@|_GDjRs<XyIQRZG9dPNJUlsMA+TI7p*L{U>vfEYU^biK4)9$r z7v6y(3<K_@*cgLntJT6+Ny6EfxDlZ6-EJ3a4~Ii32L^)y4MBPUG?m6prOb%oB-wCh z=|T{jm}<4!e!ovI44}z$hZ~|Q(!tPz9(q=StpkvX=*Q!+W=U`WPpFP}BQjPFa>*x= ztOx)!^V?^ov<avd;TQ&+%_c)SnF*lBvr8V2#|#S?AAwg$B-(qw-;ME7fSCYALX&Zw z+CCnMH=obR!L_j(2q0%zYOeFzqZg5ET+`{)=~)8-$}gI|)6}lPr~#~n6`dZ`5hzlA zfQHE|2B%h`BT%ILRGnK5wnAeIHg9AF%TGWsftv%YSgls#gsVxaFC8deexk(YAdZG$ zg0cSGswPm1{7IQ1zc@|l_fR!~vgDUi1(kr-q&%zZY5?rn&I;)LmK}34nea4_{!J%4 zkgb<l;_wOMc?>5%sX@sE@HIkRcM~6Emd~<5_4|DT0n6!12~&1e^I@(OtoDmW0`^*k ziHr_}(bay|=zzUeVIrdgVRW^hH9BCgRhY==Kp0)^XN?ZnYZWH)e{ldjUibO~SjsMZ Tl~>t700000NkvXXu0mjf5qWtD literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_d.png b/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_d.png new file mode 100644 index 0000000000000000000000000000000000000000..89aaea06c524fc0cc37af6d91667299c5b2a6ac3 GIT binary patch literal 847 zcmV-V1F-ywP)<h;3K|Lk000e1NJLTq002M$002M;0ssI2B@5<>00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeWspsM)0008wNkl<ZXhZFq zuWl+q6vnv*g#kfOC=>|^iU&X;kSGusP>358iiCuqQ79A&g(4wAkn}Ao`zXEN&6iBJ zyW2f8yEDr|hBVD|dpL7`-}y6B(m$Kcru5P;0DPB9FZ%UgeGl-=?7d?KJ_`Q|wrflV zti)f!T-bbDaTRsq9DvcgWAFe{W%vvT3i%T9B_t`}ODLm4ehL+`ge)G+PR3|3zGw;A zEh_r}q4*Mhv~t693lNMxcAarT;O!nDPD4@nqBk+1k{PmBj`Co?gp}*w7%WC_zXy)T z<9@%tTrP|<%7BND$747go=zt#B-S#p-EJF=20zp*m&>cwDu&_jSF6<ueel=qcIWf? z^?FUmV*vnKw%cun1qQVS(vNale3GzUufqm$S1J_+1F-x3o`NAOz+$n0-iV`Ct6|Lu zrqd~&AineY%qK_)%@B7`Y^=fAY&P+$Ah143dH@RF>2$F7U@!>PK)>H-LQozEno8qg ztjw^X6aBceP9ccRn5xz4ZnukWj6j3sj%=u|h#Er+<}gJkq{#rJV)UcYC}B%5fD@|Y zyBHZe2eo8cNX7sNn)PkEQ*wErTU5rdSg+Tb<zxnc8P6$sI2<%LFfv1~P)JPg?RL|7 zuNeSE!jRE%oz4b@H=E7Kpfgwv08le*H66d{qF3+Y;F?S(q8|eQ%CCmK=sJr5dH{Q2 zN70EcK%Vj=Xe61<AdU)MfIQ`=>O5+26ef>gEr6WmCjgSb!vU#SE|=<t8!9R-fc)iW zlsFvJ)et0DNAImNz*FQ8$_)9{Z7O<($^g%jKU6B{1dOKWU0nbGJ;zxNvtOK;G#HP^ zybbVgIsrg)mTB&7%Z&Fi-29Lh0RW~tr-`k|H2rWu^?E%SK+|LXW@@U+O`LylMmu@{ zBe@(H`wTb`)ySpKfRS7djC}?ih-&20XTV4<2gdK7fgoS;*YEg$qs3QU=z*66KqbBI Z>mPhmqRk>=ohbkS002ovPDHLkV1k4fbhiKi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_h.png b/Templates/BaseGame/game/tools/worldEditor/images/brushThermalErosion_h.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3dc826ea1d0746a4fcfca846b763fe3a99e331 GIT binary patch literal 848 zcmV-W1F!svP)<h;3K|Lk000e1NJLTq002M$002M;0ssI2B@5<>00004XF*Lt006O% z3;baP0000ObVXQnQ*UN;cVTj608n9RZgehAMN}YmGcGeWspsM)0008xNkl<ZXhZE< zEwAE06y+Hd1_VK&P$VQMegF!AM1jD7LTped5)y(&p-?Ckii89~vj1Y`{*<?;IWM!_ z!p_`wrY+5zhP(`Y)0y*e@0|<bW3$<m3Vj6xK1&6C1>ik_Jl*pS<efaP9r$T|l{TMv zSugWqfk?4Qg#gi~#K98~goSrNCz1~#A3_2Jd<fZ!<j(<(g1J>8R|j)i1j6{94>E^G zTPg%1_%v_rJG|5hgweko+g6Bw8+bR}s1fwLhY&;+a<G&?*eDGL2ushw9{SfV=@HOy zaD_e<2ad<%e!st5F8}iZ_aBePa5y}jPWDV(hR}AqZ8RGEQm<StuU4xU9De+6wOWxc z{<_`nd_KQkuL=0LApkAg?KWY7gBpX9!W5*$;ujg#>vhZl?n<Si$bi`Weviw+0fEJ0 z0lk5vR;yu51k>phcYyDFKJyL?;WXe5i;XdOHk(cSD@<6(#EJlg?{qrYdoUP8VxZse z(-4FQKvQYld?_>La1kNyzs4r6LJ*sns@3Xlw|m|gK*Qk<H&j+UPlgusFm5I!bO2Hj z{b)4OYzYqF3Dxn}h>V$oSc(-$NeBQm<2%+)#@Il%D912Zuh+&$lAHsm@vM@E!$Gn^ z+&S<Hfkb<6x0~_t*bY!66dB{vi6<cNX0sVN7z=AW0*D!=nsMF4=+%QPT$9N}zf?Q| zlwTEldz6|LkOSBYJ31qfrThR5lbH<qQW3*}Eaj)_JZf+hYD%z_K*sWu08HTF04tWu zrRs3QK_w-Sz5GOp!$Gx%V1jX^Zxs>9MgFkNkY9CEkuy|8ATRkN^#_@N(iG{{MFd#R zGN}E+x;!3_*$o6Oprji$)-vJ74yUmn3qD0i2pCTjhscCWu|V~DJrM!nq8U$PRV^?9 z>g5S!95+wfc?Y}$mJ0X~vJ}wzaqob&gq90@4+kPF-}$Zo+2wow?DCysMwG<GBY;#W a?)3-OeVudb8xe5;0000<MNUMnLSTXbp>qcS literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1c4d37bf5848b366375b1a9d4ce7dc4003cf664b GIT binary patch literal 810 zcmV+_1J(SAP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!%t=H+RCwCFmP<%fQ5eVn_i^r}MS^5S z(BhJUJygPQli0?Bf*>Xc!U$YQ84_*6rpL}j7Pe@SSt}!=7Lpq&O3ktq$`E{z1rf6d zCG*i^HO`%D=YOUFr_q^vjq!uag>%1ie)s#n^F6fI*4EtsI_2Xa2cl^j48wqFny`cw z5tay<1g^6!r?9XH`T6<Kbsf2qxw%<qW^TTdPz5Y0Eyeo0JoQJp64!On)7|YTvuI(i zffSxEgn)w5rMU(s#j<VaSYr{=xIpIJYQB%&rvvyr{xxO2sAMau4^^Ted@}oOrdR#I z#)dzk@zhlmY}$YW2di+YsVwFD(7-4L`-joh(Ti(s7f@1OoG}Y@Kft=sMeHgo#<_4U zDk{s;bhyTE)Tujq8Kdtf5Rb=K52b}w)fG5idpN5{sIzZ>1;S^pBf7A#B3REBL%P%t za4C4><^tSld9WflBb1kBZ&i8V8_GSep1(yjx`0^Bh39$xp}eKb#W0(2&zBEVssg!g z42z44!D9M9;z`%W+_(hC*AOZ#^t|}mTAGwVP*xfyeo*0_Z9BKDE>Bt7244>chH?*c zdub#04P`BznV!X?_AcK*>P~<Aj+*)-&?QXQjbKm;YiWPakS{QG`g`8tN=rlHa5<(3 zlcS0e49entx1$N;BOh_IxqaCfbB*7|Mka9MUKm9?9AzbYv}uYoOS5(vc8k(I+wu5K zn<{P3p1j2EmWL@iyflnqO?_ixkA!eSux(4e&9shV8b<c4;WTGk7EYf(f!edjRHb3! zd;g{aWB7OSBanNzH+^g|{Ppo9Sl4t_+E{6r_^Dn=#!x5=mjf!OoI`!zOdg^s*U16P zO;nm>;&WY7aZKoBYT~AC*^&7l^A26+XE;k9wvrW@WNLDfuZXnN3jO@`yR)zFg(HL| or*Hv7U2!cJ6)_^8&>sN?0J~cB%Q+8PG5`Po07*qoM6N<$f+_xc@c;k- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..cfea7736f3bd7f82f2a9dcb80375786a21a39f02 GIT binary patch literal 1049 zcmV+!1m^pRP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#yGcYrRCwCFmQ6@hQ547T*K;z<o>|RU zV+|GbL8kVhQ5y-8BEy1GDTuzPO$6;)whUSXQuIMYiwcVtT5W0_MG>i)We_oCP=nQs z<EU?Z_oI8>I5ux|bOfghXEAr~Ilp_(z4!kP?r3lKA%vQ=nJD)oqOFAi(ChV@3%uT( z6Muz5W+)VDVp2)7sJIv@!E+7;&X1ozOr{h<LI|Y(Lv5ut;AyBrQVN7H$vsp_1U#ms zL{exxk&`-Hx^TVX<NKcSNVq?cI&T)u3$9tWy7TP0Qyo)6r9!}C5}-O$nSuG{)jRWg z^R33xrG8&s{Vr5izQ*-`SLesTo7ZmxM;lI8pFDHy!KUi6FvfTiutfC4iQ_f%i;A4I z(+z;l7q6YFs@`li9@>YzzJ=q0z+C0V^=KeEH0NPUYq@W+ug6=mz{1Y?jgR^J&u?8+ zO%_hJ<L$=f+l^bdZJAZKw|2_&f;F)A*3~-=LqkJ!#I|vIyd;M}odxI44NkQymu^=$ zCCvhu3SPR*zv$+byOj=jWCXk7Y7X#-0x0Yv4cgQFseDt_hH2kW@cI1dOWD9cj9FHk z@YJ}P13V&?aBft3Df%PPK>DT0W`Iw$f1uQ|EMmniV%vPI%nnEM7?8Qpb;dLMCiR(; z;bDs!h9L;fQ;BzqoJ^UJkf~06hJ}FZi!{O*=A2_j>DWB*m>fM*Jmcz<5CS0*rG=qJ z1%fCAij+9yR4bfU7}O7c+A#2$TR5lp-~IR(8#usoZZFU!>?p-nu3Fxym1f#d@L9H^ z^h5G;I@+F$>&T!=h(kgK-FEOueaF{e@6RW#&$0%>+ONTHecN|#?bcGLz<}zG4dMXh zh22bGjfeL>c=7zDl?@oy=(X|MJvD8R5hg@3Mg`JfcU+nLv`wt`tJf|MA3VDM=EHlB z8~XbC7Qw@xK=7TCLWaBd*S7fsB~e1KVw6ZqnZ)o)QsM&-KYG-5-tRA+?5lv|@i=Y0 z|Fkmr<y%=SI_O$Fx3HksA6W9Crglp={3ettPhOswJv-m_cszm-;_45+fA39*gOZuQ zrLa?j8KVBcj`m?I-o?31IG04rn(LKeOcY~KQbXp?@^J70-}KmyI3#O5*}%G7hTc7# zk8LwzbDIk8hEhhI%^3!hT7@L|OcWR97*aChnWhZ|l13?&dD75{N;i}e1*J@uGh<98 z6N;22bj)ouc*f3Ps+I-%-bgGKGYbj|a^9E?4h}jEH}ps?k#M+4+kflu{|Ybwch3B5 TWKlI300000NkvXXu0mjf=#>EX literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/circleBrush_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..82a47b7489eab92033a1d5d049ebdb342f7e11f2 GIT binary patch literal 573 zcmV-D0>b@?P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz-$_J4RCwCtmfuUlP!z{^Hz&Q-pJ3pF z7C{(Bsk6OBJ@)8}f&V~%Qoah|ThW{4Oe!*ZP!o!R{(^c4@yETLBWJY6oSO@M@W5r; z-R}MD{Q7Q82qDedWN8kzIM9&OWHhq+@1U3WH+QI$B~$5RIOJjIwp$e##rvDuX&_H+ zum~K=u~>XFv6Yb4*W!QfH(PDJ@zBtr3r-Y8Il48f=#pzoFsGI=!%M)&bVge_JlJ=A zx$}!l$*5j`sv$L5uU@^v6dxDfNrhDQ0z|`SGFo<dFdUr0W!P<R*)4|{QZGO&7}jj; zXxV}r*v->C=FqR<8qRqRPM4@=m>;NaFB&g~YoJb2aH`T^vlIt~pvo+vG6C5<WY8D1 z0*&vRfNL0I^=Spa2ukA{ifa^EDOsk3XDDiEyWM(1rSWR`Y^U?w1Vt*5A`N{(N96}v ziqhr<jL+1J>KC+2fu<1Hr=xf6#rwP3UKFJbm1d0Kk3xzbA`l|<L0)!Yd=pss`LqcV z?``n902}A1@qgbMiUxCdS2N5!BCto$fm3)Vd1_5Vn54~YczbHNC8S*f^B^B_ZrMZ~ zqckS4Pag8AKjJp9xnYl*o>|&LSgKV7`g5@7m<A6dQ4904{Ug8tF`Upclu-g@00000 LNkvXXu0mjfv@HYh literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..cdbe877698994eb9eb45b1b0fb5ab8514c5c93c4 GIT binary patch literal 681 zcmV;a0#^NrP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!OG!jQRCwBAoHlKm0RscWdLUL{pb}tW zWMp7rWo2MvV`E@vXM4cT&JoB0vP4WwOhH>)n}L^?mw^$eh??N_>(>e|UOZn9vIuO6 zf`S6jc6O>;Bq=G$@c#XK1tt&&<P%z00s<g2K|#m_<Iutq7+_{*hFC-oFnBDwc<~ZL zNogsAsku1=8wV!_c6D=Sm^66`!^4LU@tVVeOX9<aj|?eksSKMoZD!yR;b4$45oWM; z(qdraLTLH*?GM9~GdCHk>W_gPlb4&rP*YvQARxd`oJAl@T;1ImZrr}c;FhAwpk^b6 z)1~_Qf(#7FDh!u*J!6<TcLu}3Lx&i4@7#r98P=c+4GUv<_~a%-e!BsKxw$Zd;P1Z# zv7MthLwt!Q!_C`Q875DeOk5m+Ton)^%ODE$FW;{}L}OKPb_P2KaiB$0h>s&nO<o2D zpi4;wx=P#(yS6<e-lCi5-!qtK^O0=PgPWg-PYm{Ub_`249cM6><zV39U?$p<d(VF` z>^S^}p{S&SxHww9Y88W|oF+qh&uxYyr{6K~{QOH0gDj|>`+z}3U7Mk{riM5lfxHe% zqM&@#y>T<c;sb9O>{PiKjAS{1K18_f`NuyD=N^A&IDGpH!<Qfb!1<`ByPLEO3$knt z&}X2MVeyj1;FP#({Uxly+|caH;P2<lATKXZddYyuka|GeO?g?z3aoPJQm6ter3cTS zKWF&=|36JEc=hTPIMuUoaIgnHdGcgEsBQ<<gdohq!a~i8@Bt790t6TU&r{_oAnl*( P00000NkvXXu0mjftKTe1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..9b24adf903dd7b0e69d7f1270620e74b0a34e6df GIT binary patch literal 760 zcmV<U0tfwxP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!nn^@KRCwC7mj7!KaTv!R%}v)fIQ&9K zPQ9!(Dcgvqxgo3u*<gZ$is?^6&_59CFE9}N$)DU0?T18*jS(bd5Lir@urO+jCbLy4 zo9qf_XGF@J+UFx(x!cwhx;MP>j+6Jx=lMK7oFE>LlK@~xCV(F?$g`qqwQARBG=y9( z=Y^8X<%mosV;9zHH9{(t0{Y~|zq1Kk5nu}h0$wV@Fq~fpRVo$!K^Vd}2<QK^qHs7& z1_Hr>L?S`aG);1!Yqi?+@bFO5;c%=O3<jp<)@8F<iPPz{t*$0?-O?_g^h)R->xK4? zwm;w5DV8Dgev_JiGEW7AkFJl7US0BdJW-`mSvnB3ySoe6EC@qvwh1Tm{TpeyWrV&< znnT;tr$a^F9E6qFI>aI|)A+cR3Wc8f8(|oP^MzGZ4GY6YZI~?X{2=e#wZMh`9%wID zj`W~W3zN6aP(VM<<#L%C^RO{PRN=HPl}b}LCXLW_4BeN@O&|J6StkOXf=J}$Wt6wD zXBgP2Yiu3X`7;VYPg|a_(<&(}Mw1epw{PmWXH>5$`4!HcJ<&2~GqVk1vADzq;rsg3 zHG<S?b^2LkmC~tYa9rHhJmgCOV(}D=j9hum1!0De{ll`Jp7za&+B)g`zE|*eWdkHN z!I8!Q-{U3d?j~rr+r7ZethbK6V1>4LX=dhue`@NUEf8MTJzx9)7kf$2sbtV0`CDPG z@Dq|7+wg9khGMA<Lqnz|uh;vcu^Ltfg0K_{Mal1fI2VmZ$xtXXu>A286?yY{-)2y$ zdeYY{V@a#kx~A9b8E(!V5W5);lG$vgv70%g!!oe;ELA>rWJUi=>{4a&`8<Kd&kF;? qU=Slhb3VJZwPi=<_2uN300RK}`5z?~hH|(70000<MNUMnLSTZ5r)9+e literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/clearEmpty_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..6aed6c702a440289e1f0645f669e7895ee917e52 GIT binary patch literal 636 zcmV-?0)zdDP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!9!W$&RCwC#mAh`*P#DKQUldRVKtigx z2?@l4K&qDxbbw^ZkSP-x^9C960!iMWdn@G;TDEp73n~mq2(=RzMFIu}L`c*IY;5O$ z&~ap0Hn<t04n5LO#~h#U{LgnYf@zxc8bhR48vlj<eIePokj-YFpC^-vg6Rw#0L8_= z4r<^_GMT*f5*ZGMBsj~P2|*x&#~W}G4u>h1%TX$oqCg<<yl=HyQNQ1(!C*kk<&rIZ z0M`(_^Cg7Cn8^2FFsPKvWy<ICJHyiHbg0#8F~JUg9y-OCknCdjITQ*h)oOL)!Fs*k zDO4;LDHe-StyWVYc7df!VD2i0gHowPq9~H4X})10E))vP{v7N%&vx^O(&;q4<kJ+7 z$JrDHLGF=edFCoK9*-%L$^5m@bUJ0zJxet(fX;CZUA@t0kgn?_%d&6g^Eq|9U6Lf} z6V8yh6OwhPSJl-~b#E(`$|ljfM!VgnX0ypk{#PQAII=nbJOI_Rk7l#k8yMZ--Lch2 zEN}KcS}Ybc8jYye>uq`p#q$w4y5g_dp{%)nZx!Nh??dpNs;Z~h^BxqOo(O{Q1EKnc z3w(`4A|u-?))SB556h8P9}!RBDjJQpG5_Sb6nxZ!Y1wj&t>tYiP2!vPcw)T-4O^9| zTtu@st-R%S1~)Rek<1Z2BCxihJHHIFk5Fi~n_3<Tjy<N$E7RBC#1`+Yo4;s02`~Uj WvibE+j8>5V0000<MNUMnLSTZp2p>fN literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..963c1d320c5f1119979af2deca6c4a341dbaa44d GIT binary patch literal 434 zcmV;j0ZsmiP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzR7pfZRCwC#mAz_%P!z}inj4jXxCM03 z3z<TeLKdM*mp*{BZ_}xJhfcnRLzX^_f`e!p1?`e0L+Y`(i22ZJ+o69r$VJcnc|JHt za9#HXfL9vFKoSK3Y&wM`NswhJlx4+X1ewq0e6?CZRaFS(M?)%2Q@-6M9)T(d=e(rL zgM)NkN1o?gqyt4V282X+QVPZTFb1U7WHR{z{e=EOe;>q7y~wf*FRvSH-uzA%K0n># z(Yd=G<oiBuZ4(bIPN!6S6v*BK91aIe%Mo-DwBPTMBnj5*HB8g&6p`nJm2cCpwMKCq zSAWzMQjZ++zp5F6Ab@3Ay^3xaMitRT5H$g%Zn0SO9-^csOl%qJ&@>IR*{m0cQckd< zD9$&-a=EPRJF;#27bior-$w(LODUwn9goLR0K46e9wAc{*@>daquoxMkc=@l<RT19 cb3O$a07hbA-vWNBmH+?%07*qoM6N<$g4qSMuK)l5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..c9ce739da5a23cf682faea46de4aa450d479d3fc GIT binary patch literal 494 zcmV<K0TKR*P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzkV!;ARCwCFR=;b)Koq`c5J?A*D3Us8 zmeM$sj&6lc4pJN&`WM=6j)e|xrOlEtrHh#oe;|htO=%MQUhzsa9gJ{(@a2-rz4!6u z-aR5&mZcFwOdus<z<>ieNfNWwYSCJ)CPZaEpHtU$&BA80p{lA97=>_~o1!Cvlq5-r zBNW5@zd~^wix)x>(L$L2bdF9Y6h#q1iWI{9asqDlg{IS~K6rZRzrKIwI>Zn6H}+lc zc9?O5dldik=_Zp&=lV)#?e@j+P?6~SOFL6YcfDRemrA8@rx<wEr5y!9Kv%1k(QKX% z8;!;|hoP};+g;!H4T!TdRky!K&@v3T59{^1jq`a>E|;C<a(Rid**yYVX(3qHQ4}S& z2m<0h7K=qc9m}h(zz-}E7U?iz?|VznAIeIl;$ZUGY-a4g`|Awb{n3lXqA$xbJH`<p zghFt_Fw`L4;gcDmYPC9oVZ@)0fu?DXJo%0g?+hZb(^N&`w2t7ypxP)v%nt)&D8yu_ k^MU7iCQ#_l$&Ua709~lTke`4oQ2+n{07*qoM6N<$g8!G?{r~^~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/flattenHeight_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..d8d1931ad15d243c2b1d10eafb32098b78d7f55a GIT binary patch literal 439 zcmV;o0Z9IdP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzSxH1eRCwBA{Qv(y13dr}1D(LAMWYrC zV~bdjIZ8@OV7j-r*WvW()AMiMys1EnVdm!M3uerikx0-YB#^JFs`>zEQ6jOHsH>|R z$ji(3107jdTwMHtphZB1AYTLs2nZyeKY#uZkj)Ilj8Kdd0M#BhH8t)3_3Kv%kh=hg z{{r!Ud~p;2#2<jJS_GuofS41CISAoDfBtM`Vqya6XNNivZ(_)Yx(K9@6{-g0y5EE_ z(4tK&EG*toeJseKz=RfT4nRr)s9_S+A||LIKZwCVum)sKZEdX=QjlZy5y+wo<>lp% zkmBzT)JOja;pXP%cfe|ABNrE!4%BS8`HaZ*5iEH_<Awujzz?Wpq~uLbs6#<|e}NJO z6ApziZ~uj2Qow&CJ@DXT!K3&O6#pl~qCZdv|HGGI|0AWOe`J<8ARqliG7qOke<>{5 hFw9`2SK|aAzyJ*djA3BA-zNY7002ovPDHLkV1f$=x)%Td literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..79d2384d7f24644e439bafe9866001f35f2becc4 GIT binary patch literal 757 zcmV<R0t)?!P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!mq|oHRCwBAoHlKm0RscWdLUL{pb}tW zWMp7rWo2MvV`E@vXM4cT&JoB0vP4WwOhH>)n}L^?mw^$eh??N_>(>e|UOZn9vIuO6 zf`S6jc6O>;Bq=G$@c#XK1tt&&<P%z00s<g2K|#m_<B(#>fg5i~vW%IT8DbHsz_IHn z=?R3a@c8?OP8R+CO)ranQD)JCBaavyI}bAyF2Bt1;LSJOx_<qn%pwI5P6m55VTKK- zA2S$M?_yYR=mA`A;qDs@ES^)rnEBT)IQzmq%oKRx;Rl9>byrdJqgdpiA<EDYsKs!< z&x;|zP=X<G`Z0zJ58i|2;$1ZuRD?JfSb@HQVP{P-1_LD#gatsgj!k<Q3@f%VEIe?B z;o^gLgyU#_h6RIxG!H}Jyc2M-o(ww%wx7S?xF*&d5$yYJGZ+?cWMKI7hv9s?2gChI z{tW$bM#LqC{&*9H1Lq$x9Ju@p%=fWXWpL1yVqgRMCeBTh!PZa?;uB!-70x=skYK04 zu)oQJ!9YQXv{VN&)EO9@t&0%8Do?g$VE*xop)AD?%>VG^Cqroc76xNkeunw^4&*2A zx@c2|W2f&i9KP@v%(pU8WLVH2#ULvs2<A^)bC%)j`_Bw>i(M$oM|K9X3{E<d3^~mk z89sjg0T%Oc)&|o@&OK!4o^^ns78qCZ5`2_r*d^`33@pEXGdM*}W0<w{7{jqM_Zhlp z?q^6WTE-CTuEmfUZbr}y7Gf;o=jCA7w=9#PzH<vh@8sPKpFV$Qkd+Z+NDnh*D9Lgm zF2~NEJ^TODr%(UW5}Y`3;y)9xQhM<G`Ev#!pMe(O)vH$^>H!M}2YcX?Cr{Rc>UK~~ n2*NBZEYz%RLFG^&K!5=NVS+cB!TZUK00000NkvXXu0mjf<aAC; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..07e3dd25ac51314bc773556499739f922d64fe90 GIT binary patch literal 859 zcmV-h1El<kP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!{YgYYRCwBAJbn7K1Oo#@1P}`_&<1<} z;uS3a{{4%PmzNh15fPz@EpOhu5qS9UVFc6v|NjMqgoGG?G)*wnYyl>qj~Gb7YxbX4 zAXN_tgDv{|_b<`7b<wVPQq+Sm$PN}5hmnzys4)Nk52Bt}017^kdKQ?Eh_>j@ACfHs z`-oJF{{8t)o<)CvE(GCOn@<`Xz4}03N|^6OoSX4UEd}upST+9s4OUNzMIiMo|A4Lo zF(id}J}5~DJUn;nsea+)4F&-=+B;KyEiQl*&s=lBVCBwJ{xHpUiEZ^Dx+yDQrj4Q6 z3uJ?jUcN7}|NLDIaY3FBApKw;f#T>dH0az-RUd#bQ0;-D$s7Gw?mg$PE-UiFT37K! zN}%n99S6?afByO%UV6xg3q7#ZQ+@#pMvw(_x14l;`sSkoFDLs44SBH}AUSYi0449g zP#+=Vu1xQZLR@SwX0Jcu4igJ;G1|!tR13pF&PE4eax>Q*HfWr^E&U&mT^-{*b45eU z#D+w-oiKTj9Zcw!z;L*e{>~>a-YRT4d`SVs4{$ZPAtTK90Az`lg2V-9EA0m$@x$kC zNo+iD-d$Z*=)#QBkcDQNGB2>12hOnABwS3@9!LvvKUlx#tUXN32j~mN-+vfl0&T#) zdHLo8$K+*u!$o;HU$kfXZp7v2f3Tp#h5hVw4!#8XYS)p=3Lw5c&=;Y8Rx1_d#Xo@f z3)UUB`2GC{M^2dCMna_n+#+l)aWDb;N{IKtij7D8UjbbY67zC1xdEd09J!=$>f9}R zI|Jo|N-`oJaQOyiK65}ofCH$Cz*YJhYRTO^bmWT3z9W}SUcP$I`2OP;mTh~^8SFlA z(Op?q^h!>G`(8pN_^Vg15G4aHAi&4<qqZn`=G;ZQ-A|mnVSo6@6%H;g_7D0RvM1Bx z-3|~_N}v>~_y9jI=Z~Unzl}iVMr3hfiz1Xlm6iZjsD60&?wtS-(5Aug;RDE`4=ljQ lTJik(^9UfOY2ySSzyRY2Y?uC_8)yIk002ovPDHLkV1iythXMcq literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/lowerHeight_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..048615dcf9dc66a4309459d3b301e3d356f6e0a2 GIT binary patch literal 728 zcmV;}0w?{6P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!dPzh<RCwCFR?mwPaTIsPA2TjOB}jNO zMMMO_60}2vOLgkfN$67B(kZ&^(kVJb-D26c{()TxqFX=C!A5m4v(W+Z7t(eS>82Og zah>V&;0;Xf&YDH@g~uDm@B4n<`@GNls$dv~%ZVkr99oV6dCZ4cEcQnM`Fy@mDwRGs zQPHj=)aklzJ_`R8XgZymah^ru{}#v%^RZt{47prx1@0!SirIQrE|>S_R3t8vs9+9k zu~;m?6&UO!-mg_El^yynl}b?(+q1!w$t2(DbiP>MWwY4~+%?#q8UGs)%bOZ3WHOm; zK@e`B@#ku_n!;e;vG<JS%ukI*;|lzkJ|HH8n5Ud9vGGPxlr_5^5s<f9t=<7eDuA}Q z50uclb)%+fPndddI-S0Sy$aC^e-;2y*gD4&hA<0cu(?K~(R~D6Oe7MQX*05a3$TwY z@mJk$w~WUnFp&emCTvGl)nzcC5;#m|^V$I&!1lrYdmQ-L?RGz4g%QAGnxnyBuu0hl zgTYghB)voewR*k2$`wcq;ImY8q-tm)Nm?4Rd<1_X9*-|mP`lmUg#fPi`+e$x5ekKF z!=C`qDxW1W0JUI33DY^WcUBCC<}8^^(!CZL4u|J^z1~mGaw!^(7Gzm|jre=bW^;=H ze4OZ*nbGZ3cW6Z-k&xf-|4RE{&!Mbuczp@}E<V3&wOT9O2Z=-Wxf<ihIXdR{7Q^B2 z=|CW`fy1v7Rj?nh7ohSX(<F1HW2Q>C9oRWP6Q+tpKFG{iWV$@2h`7J@SJ0ewzyOj( zO@`$fn%Y&eD3HNbnAN#w#zA9m{40<tdyTUfEkHWkvHx;@2`~WcEF|Gcx{1U90000< KMNUMnLSTXpwouOi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..007650c79e8b9a9adc27ce133cbbb34206defc69 GIT binary patch literal 777 zcmV+k1NQuhP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!t4TybRCwCFR$oX{Q5^oZ`)3-oShIy< zHM<&!PWpo{y-5F7PsMu3z@P|1D0(pRVIT?;RA^MFBglsyG6;epDL%L+6oeo_xTz2s zm#MRsZvDZK++6o}Vh=+1ZuiFZgTuX?^L_Wb-?``9^Rom3f&Bo2N^`>mSS%KB90#7~ zAqt`_h=PwN!(yB0mZVBJ91d8mR+xoka@oC<Oa;l58P<9{*lM@4H!=(9bQ+=2Q8$Z4 z3C%Vn(DMaB05hd2HQO);(Po1cn+!n-rz_saDH-=~K0!iG;d)mS>+5M9fXnSbOZO#I z*5uy+7G+ioO8V|i1VaO_u+3@5%E}6A8zhufxLBim>Z38@5H~NBeVUz$M6zm_c>O+m z_Fyw>iTGDl9*yOAq*258_;@yN(gk@D8T^LtvKKo_OH_F@MjYaj#&FV^$q(7j3tw0( z6gfU=)skv1q>h%mSpBt%!M9HfUTizdTsU+10*ba2;d!Y4pB&BUotv9S#lCXg+k!Nt zRsBh5iQ8`9z#eIrZqq9*7vVj9RCQc4X=Z&5+4YQWQ__&ukg3150}s1;b(@lgw1!Mw zrA|brr*)f>hO~yh>4Uy5I7_x;J~oH)-GzNy;=XwK49iPNV@c}{)L~?31V<XYxOb;J zuOV@WOB&J|GG)tOs=(UXDtdbRloT<IgfYe;E@?E+)qJMMyvOml{}DdT&O-U2ph~KO zr=}Wh?d@#*R%a(7Q!@yML-6}su~(9q=D$n{Cr+LGd)iD)pt#tE!Qo+?YiwkFbYD|l zjYJ}W=4)3As~|Y8@N@i7eLW_l(G7j)o0@d5ZY$4iPMNa7RH;KZ-_$1B<kYtmD-X@a zVmwY4nHNOS_x;Du;MiE$EeN7gg$v+#j;a|md}XEi{s=GtM30(e#9Xea00000NkvXX Hu0mjff*fZH literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..a7764100a57eb0714073dd75f9e8e6e3a963f7ca GIT binary patch literal 982 zcmV;{11bE8P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#cu7P-RCwCFmR(2`Q547TEK3cEF>9+c z>&LcOk)-JMK`BvE>qk&w1fmbdCiIkqFZI+z_7L<1v4nxW*rJ6N)-DlfVWAlrB(4&X zWuoiH%&6fM8mqf=r+a3VY~5Lv*u8Mr%iMGJH|N~{J##5fZ!d=sDksLE=nsillOBL9 z%Q8lmjXH5!6d6$z%k@~njA6?bq{xA&LxIB!Jfl|>gk)JpN(^d?i2;v56@;;j5T%Pg zRKW;%dIe*IWrEk?hykDb2bm}JuCm{AKCY<bh!dW@bic80ilc?n%2Um|vbRgCLa~B? zr^f&#nPLs*hqoS1m=Kw~!~2Hk7v^arm*8QOjVb`<Ge2f5j3M6F^6*6lm&{5B4q1iB zU;-s<Bx?}6J9;=*({nq__aC%6uH3ogSiZg^^PC%gCnu)puGjBMU>*qgow@mYHNn8s zshNV1!Wci!GyZ@dW$npqOJoy#iiRJofe-jXjFum8fJb$8b=7Q3Pe&9*{qshj`Ue<e zqQOTIfw7#?w-%v@!ZfWAu}n|R&{^3#SL`u}==;zR2q{=zD#XEgzo9G;2++4{uQ}jI zt!2PVUaY!Yt=H>)*Bw{uR+g3+;gRMbtBMLer$2v5oGIDl!5FXWSZrqEhxrNKsuc%3 zHPe8MkuD_FV<(E7W{X)`FZAT;;~m~%Z(q1Lf(m9TW5`rSp#@y<FOZk<qqUZHX+R9b zYQ+IhhZpx0y2WyP*7DTe{OECh==0SKVjwo$n#zGpL2V#q6r$dZ^aeo`c#ABrue=Zg zv0>wgdVxo^wYJun&0ANjdwTnrBsR$hGEYfKUel2oAD^J%=uL`~v~_n**tY2Pj{I$d zp(8sdOL|c6%xHhrUXq)a+g5eqyeAT{_U=7v_xtW*TcOQ;w$lD3+<z{$L0z!kN?7Ae zCJ98*+1Xh<;vJ>6{9p}yz!ze){DP)7d__}fwt}N%Z)E5A*cb!*US3*OO-epoai*dJ z)*Bld^GPW!H*VdmvlZCfDXFQ_Voby@&Eoe|TPy{IHfY{L&&M8yv@PG)fdT8_x4}MG zgE?a`2t3czc2d%%@<kfJquef6O=?;iiq#k!9fdZip!Ig0|GPab-5g>FRc{VTj5Ag5 zNN@q3ot<Uk;^LwXoSU0dFWj)FO_C&)6N}jm|BnCz0IW{B?z^m?nE(I)07*qoM6N<$ Ef|#DsIsgCw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/maskBrush_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..99bafbcb44c44c671a41fd8aa896875e232e0a41 GIT binary patch literal 822 zcmV-61Ihe}P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!*hxe|RCwC#R?TY@K@{KF-6jQzO{=8# z1G-xACZv}hZAt_!(n4)T!Go>%A9(OmXs@398%hxrk!V#?B$5>GZJ`LFDM{OlQZ&Yr zY`Qxiz6mcfYO={H>Omh2Z?Ze@{pNlBHbe-a{b|v)zaalXf2){{UYxX!fAf3t@)ZkI zLMM6@^e8?~bHO*{>%{$gc`u-BKGz(yCgT^UC5R=s$Z*cXjIc#RH+D6m5yse}CbSX5 zG)fawm!{xcZu#MZwPrp^qmhx3o*SQtg70+oAR#A`%a4k9(V%<;>p;FcJ9Z&rn&u{K zrL*|TlgI10uN$dnO*!KhrBvgb-2}igJPM$VbjVSQRPtFt2!0FBLJ)iq&l$cWoxOfT z*6>px5FjsBk`k~TK`IESDY|<3`1!Fe+qQ#kZGrG=>iHXZ<kTW=DN?9c0@gYJ7BQ95 zn95hEDT4CaZ#eq_^h-+JyJ$mvjg0HMQApRS(rgv1A@`~X*kl~XIjuCR-tx#ml%u+? z?_$%`(#o1NrU1|dXu2izO_Xdj2d&t)rW*!PQrnV9J81Q<JyS2mUNud-jx&cTb(*AA zwVP*@rqHHtIZTh5N~Rg{OL1Tq1GVu}+zU6J1U1}mSrKJEJ*978APjaxz+BU46Rc;~ z(9!l*HeW84&QO<b9_#DB+6=I}w|9n7maEw1SU&r9yVh9s`oZ(|P@A0N90*vqBRxG` z$TB}P#?eT37}9zN&Pf*ALBG^@O{MI;f4A1r*%^oJpnKp1Lx(%IV4s06%R=?WM4ty0 zv+eQ_aPKVacRm*OUL$)s3W2l^Dy3K`Nb-JlC=@ycl=&I<*I+GGGa=ZFY#Nx$BXI61 zM%b>5neg>JRR><)xU|17xpeqDyCCWSf>DKU>92rl$V40?FqNSvN^b~-hlLlRm_9~O zC=qal;xy$m$6Fy0Q}ZwN95X)-$UpOE`-=br0Cmq*G@kNg`2YX_07*qoM6N<$g4Ahy AI{*Lx literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..870db9d38e0ecc2e911d94f8c90d6ea73254e0df GIT binary patch literal 697 zcmV;q0!ICbP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!TS-JgRCwCll}%_9Q51#GB$G@QO<Na6 zViFz179<;Sq1g(tVu?ytfoc&KBB<a(vHqY7K?o{hN{I^vg|=exCm@1QT)LQux@zn~ ztplBDLeexPQN(I|-ZxEx1x+Sx>cRt;%N^dl@0~l3xk5Z1-wGf>A)`gI2m(Z#4R*U7 zl4MsU$q^Pg#N~3ye!m}1rxOA#YNc|yoSey|6C7kfWLYM<)N+v9?M9(ckS)C6PF4fL z#rNbbv{Z}LfK+9*S{FfP^h48;J8}%#hOS!VPYvK_v1smsm|3(ykf}ZU@r82d?ORws z@y5vW$}6amew0nXOo2X}K8ppP2fndkwCxU}oSHPUZ)^l}x(@B;(8@t`N)j{4Bs`bS z!`j}CGP%k}4jS3+^<d-mDBj<_XRKQhXd*TQDX;@;D7dnhui&$=n7P*W1kpzE^5}7V zuhYW1pbVYz6S?U=dBUhmRA<ItphP#+$Q2jcu0>&?);{m+M~R#^1^Py(8$UCEj=*-T ztGM|{?uz858ocJRQ%ig9592Es9)$Kmi47w6Zn80GUehqrcK~^U?YtT_p3Tzfrzv=I zs=@2oiYpF_JbQ{QhY#WH<0rTi+KW-zqbB;PGF{gh*XxImSgbe1&dHnDAmy1<iv3BW zau;i)!t^vtSKe0?g^9mA>7ZNGy(@^`ix;pt5cuo2D+#ROb?)?fm()u@eDivM8VIcU z(XpI=SBd>^pd0E`^B^0I+<zep4J9?5PJ=C@9cGlxW_eOYheHZ$nwH?Rolim@qA0d< f5ULd6UjhsOcBLie@BSyV00000NkvXXu0mjfaD+i} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..6c95d4f7f70bc1aa86afae24e724718861b3e6c4 GIT binary patch literal 792 zcmV+z1LypSP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!x=BPqRCwBAJbn7K1Oo#@1P}`_&<1<} z;uS3a{{4%PmzNh15fPz@EpOhu5qS9UVFc6v|NjMqgoGG?G)*wnYypsu7(kejk&z}o z0$Bz!A8gUzzkl&s^x@WRf$MYUyMKA~NCPIuucmfEH#ci1r-;Z8Vk}|;S%MG0zJ5dC zNK;Gt=Z_x+<U>PNT2Aci1krCFJytkWT^IlP<tq+CW`Hmg%tyHJi7C_k|8Q`8FsrLs zsNmyu<HLnZ5+K^Jq;TQKPag$N&zxmX$P%!R@L6<Y*KUQ^&z>r%1p4n}7Zmsb3c_0p z7RG~UekH{Z$^rfxADlmL0a8cMqCbEBFyO-HH*T_L5fyzPZ)f`e#J{#+q5F5BMi33+ zt9g0e;E<NSab(_t@DDFua^N$I=^s!dHi^r-cPqSn_Dn&|!~GzL|6t#Kg*Oi$X?zE| z4n%|aAaT2byp8|4I6thZsf~XMj8AN4f$U)VizVpZ10(q0(q;Z^q9PAefW85V-Q2#z z{u_{nVGtiA&LJT1!>hJ>;m?168CEwpr*H0`;BfQgNeL7)VW|$;_J`N63oNd!jsN`p zJBNKr@<x!@?fnN7UO#`X@b&K>1{enMLE<2JPJaF$G5virrS$Yp-nn#9e_c;+`s{+j zc$k^s?6Y?5+6EmR9ftiYRv278cHI2y`}Y#Of`Ttx<Ks7~nHax7&lfAxv+BcVPHV)M zcb?w9Bk=X(M-GsFP(HeH<q9Ojf^6j#5&a;ptaMXRTW6=Cqr(G`29P*9`2Oz?R&it? zuBh+<rXOB1z`{`v7#AQ6QUK!P0^gwONhyLr>fsg<3%>m!>?3qQwne0<hg$?I8HfeB zQ>QeNY7t5t(Grm5BXR(&KKk(P-8%swpiP6}!v~N>A6S6NZ^iTH&m(}Cri~MT00RIN W90f=Ejmud80000<MNUMnLSTZTkZo}Q literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/raiseHeight_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..e110a52472c85b1613ed2533afdf812f7b87a3c5 GIT binary patch literal 728 zcmV;}0w?{6P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!dPzh<RCwClRZWOeQ55z${)BA|Y12x? zT51@mFe&4ZmS|EN-3T?%E+|~KDP}XfXxXwBx^mS>ff;D6+|)$sLUAz!l2Aw_g!=ya z&T#JVvgSvchUmiKymQ_?-*<lQeNWPL-E<twY&xd({}EZlgU939b5lN_cbm=TN0KC6 z6F$uLsitX#VzK!3NGi(Za&A>sw`5s%D~fU*a*8zU73g|4n{^%u5dfdeX0vU8#!98q zrp02p0R3V#8eN9p4Oz!F>5qs~sniO9NDAe0`CBHFIg1v)N+c5Js@3XnJRaXh0(au^ z_@#re_I(M02arakQh5W@=5RP3!Zx{Ft_|2qtybHdOeW7Uud>hQJH20JdlE&X(Vc40 zI%>^QGMQY(tWrdTDz2isWy)nZ9Nxm7k4~raWjGx6EJPFx2Aw>cM!jCAYOI*eJt7f; zD(+JP?RNXiU@*7>9}cDTHWUi2BmVq>M1H@YPFH<8of6p^fkh$_s_Y_sCLv_Qze?@u z_xqol&E_)N_!#~hh~EkX0(B#<B))a&bowULU1UYsyvNYjTCLUxJfEQXP@70%v6zf1 zX&zQOl#uU`Dc?yuVR^mYGj_ZEWT(@4M_2Br3yUnap`L-L(Cu~`T&;}(HQGWxY8%H% ztZhsvGn5S#eu7pxNs3c;iF&;rfxl(~s{zEs)lh(iiB!&1V=b|kSyTLyVKka$ZbTwW zu~BCd0ie))xLu-kGXt?8Q#oEFpwAQOhD=qiLS=jBJrgOMtzu}I5kqAW_v*Jq3X91= zu)8onNeulEOaJBTZCKjB^gq((T6*~Z(Sl1goVdSAv~YWW2rvNsc~s6+ep9jl0000< KMNUMnLSTaPlTWk& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e97d7edc971950fcb2bfce2c4d061b53d2d11d63 GIT binary patch literal 855 zcmV-d1E~CoP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!`AI}URCwC7mS0FyQ5?s==ia+}SJZU= z!HA@Vm4=Q@f(YuNhazg0X!%g|l28()w1oZ`1|=2|^boPAx1M?w60y=Z38WyQH1r`@ z1ujtY(qeAA+ivHaxxCx%ouljrXXoBs?(cJczu)(q-z9-Szzbl24j0zQ2qCc9Y;;Ia zWF;soN{hrG>lE2lQ?ngTrxSu8U@c8mRoBn(uK@<)kOsFK8yyZvlC-9vL?VH)u`w54 zK8xgkK$tO~ilTsnSh{i{EYDVUv#SI3n>L{Jz+QB>pT?Wfmjz*>DB>RqLNGHug;&Fa zIDe*%Lkj@~GsR@0F<u+(_QNOWynF=}l~vGn9q!s{EQCJeQpZ_KSRRPI$VTpUuAS@d zf&Y3hUr0q|8BVtOP)*a><G+YxGPyda7z=&-f?NIz=<mITa)%we>gyng5<H#;%+GyC zG!}zK)s`g+D`#erFu>#c{TLn^L^2kE*RvCz#(GG$w3|g$<#Q|=OGiPYWkM(j$_Lz` zfESR6hCPjSG>Td%c4b)>bYXOikAL8Inx=s;CDSO22C?Gy_&X6+=dgdD2j1Q89PJSb z(eY_=2H{8?o40I5ie@8~N@iuHAn5tiM|g0rAC)B%PPDh8w8X*J&IF)mB4N(x{nW>4 z{_M!HX4CXrf~F?kambEs74S75&b-N;8Y-F28U63;tctSoDxB)*GTro+pu0D&!m%DW z-f}b@g+TH+dMf=#QcFOj&*SkG5eR}<9BAVGTb`W0zQA&+NzR;aC8f@EBvc4Ci5@$m zP+X0MMu&I=SxVP4(dk($_8s*7NtZ|gB2%29<*c${4JQMedF~A5$vQ!@l}LE{3?Q<a zzV+oFVt%TdZEUkMlI|(VvK==aYRYkBR-cs+pAC*$_a#}Dg9{7uF4hT+J!vu&#?n$0 zk);S#llIrmBs4qArl4e}9}e?B76(RNkGK@Y23fIlAd4s-kMjdEp(ToTEC}qwM5bL; h&MZ7gN6T*k1^^i?5Mk(w>FWRh002ovPDHLkV1m`whqnL# literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_h.png new file mode 100644 index 0000000000000000000000000000000000000000..1dc2a56636b30d5c49fc9a936d6a238522c33913 GIT binary patch literal 1072 zcmV-01kd}4P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#(n&-?RCwC7R$FKjK@^?Y-K1Mv?Na*) z2tH}7eOM8-{ZoXhr9SZ4ha%#K;D_jkKMGp=<EJ8u72j{fUn!#asrYWxnzSlti(nHh z64Rt<lARgvolR;sNvl6ym~66h=bpLeo--Tz&f{?rLRw)MWNNM;jC&dvqSfVc8Kvdr zQ-bm{5HS3Hf2&4_Fp6i+B+N9Y1PF8c{X>Q(xFC!%+hoPO`5j-reeKM(@Zi3^t2{k# zSC*8N4wROa`PVcxzu(%n(?1y)VrZOmVkS(YDAYE|Gr-3e3H+pFczAd&jy|8S;m(a~ z1MR!_JlfLImIcTG1TpB#7cWk@7rN!c>dLB)%s_9yFRQYX9mkeDfA)0cSaj5ISIi*+ z|1b$hV`KXd9J;Y-+x9H5&!0XMg$t<2WU_)@Z*M0KoJ@Vk=b7X1(PQs7Y~Io>pt9oP zVzPc?6PZ1$T)%wbd`mnQr|GdWH(+R-aoHoDu?q~ybqx-D)4R@`Zs_xVtT(1lBkoFz zjjEbO<ZJg6V<a3=^L3qRnkq>avJD87<S_ku<LT*S8PC20z=gAC8hqZ~dP;;|UF9Y< zwbevbRm&{F-|`*`g&deKKVN4sCsKU^AF)ZL{e5siztnZE!S}JZUUzExvZXb$ioCo$ zTb!rogA~bVG>XVVQ52r%bXY)up)n}SoIqiZyC1l2UAxl45^-b2@;b7(uGStvEpGbz z`bi`jCxwNDKNE=rjcY13%>-3dMTQ{;_=rmmiCx{~`o*rw#}B(Vm6a6fs~ek1QPB*L zwg4L_mc}WSlq+!m)c*yR3sPNM*Ta+faPtnNU!ae8(ns8cz`sbI*RKo&saP;uZ)|Fu zkX9wxpP|4npfW$Sxowvh^D)k3auk^$mIfpcH|>qTdG*p-V@83#u6dp0hNe~V*X>|J z_-#Ue6<HnAP|hd-*fdN-XOha5K&UPVvH=B#epI4IPM_*Z3Y1D}M5-wyyH&cbhcO`n zX{ySsy;ymfRT0Y)1E#ur|4#1nhtjOa?K^hL>R>AZ?p#q7jz3a)5knrTg7e9bV3DjM z1(E|0g_80l4k7q>@#BXon+PbC5Qh-|d?iTZQ;Lg3dQM9WkRTg~N;-(GEB5}-@mY)P zgGY|vmQ?{=s5T|(A<MAB@{wS6pz~L6b;jZ`btD{i#$s{pe_n7FbzB~xNLzYN=_3dT zhDJh0K|z6SA-Y-QI0q-YG))r<RfNToC4@*Mf}02;b@zh7U@HvUpA+za7>mUKhyC|E q9uy}=olfR(IC+}?<)4#30t^8BB5dp9T2s>i0000<MNUMnLSTXvGWFm9 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-mesh-road_n.png new file mode 100644 index 0000000000000000000000000000000000000000..717e603569abef505123b9de8c2d6479b7c095cc GIT binary patch literal 649 zcmV;40(Sk0P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!D@jB_RCwCVRn1NtF%TYmSBapIIvk(@ zS^^sqkqC)vqgMQc2jCH)z4TVzfWARr00{I}F7O8J8x*Mmgg}7QRv->73W&cZ>&_4_ zcsI^MR4KyIry0lge*Qe;F_Th4Ga82`wWbl-dS^JB^ISSlM$X0gxo<&dBJlyu9O5ft z9kG0Tc(`7VVE17EzZH!kW)a_z{0m~Fk)x9eLI1Yl?n%)b!~yktdY?l}>k}dkB?}TU z@5sn9&Y5Wuk}Y#?y(9mrh{Jec5;~tH1a{Zzol@x0N!P9U4|grPy1VD3bU&jK7;qfm zoWo!`O~ZYND8dR&hVPpjH#U{*XC?v_llQ1bBnKSGWHMme)lZa4C2wkrvR4S#NMvd{ zK_nU#PAUyf%7I8E>YKN{wduJLMNob)kqYjrW!cH0Tn>h^Swl!A?C$Kq^%V(#4HXVc zi*+~=l1!l~ig<^}zj_U^SUXU)V(e}`l`rb=@9k9s&aL^tF2aeBBuON_FZ#sj*m&>` zYyIl?PX7ER=f}~npDTt5p^84Xwa3Inq2T2?uEO1euEwPSsFW)=s!*Mm1QeIbLy4x& zPD}8qxY$UgIMRsz>WL5<qlMy9?Y&Z}g6iJROnb4DN=-RPRQJM4^$Ys?xX<3bYtmG{ z?k%c10rMwP4S86NWTqE3|1#h3vy1hwM-f~i&QzRkp9#CpBBp9qo3fAYg*B%3eI>}N jSB*3xxmMQK{s}MuaD20kb0gsn00000NkvXXu0mjfC$lHs literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4d3dfa75957d6e6b52d7ef593c6451526500a061 GIT binary patch literal 760 zcmV<U0tfwxP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!nn^@KRCwCFR?BM>aS;Bp&!k`>=Xwa# zq6Z7Mf-e-LcPUZ4RQwA}5j{x`_99r*Qat4*UP2F6jJ*YmLCi^fBq}`=J>*mmrW$P0 zH2aD(zqm0?6L+hr1H&ep+5P65nQwjxB$LTw0Mg_U^vME&08~{aSD<TpQPcIP!XP1C ziwqARLO2|TBuVI}mCNPGkIhP&fdt6W(NP>29E74MeFd^C3%Oh_BGP9j`vJnn@6>f2 z0*K4CA0V#DvJA;@bN|&O{3nyVn7C}*&xT?4jQ^O=<JQd^dk1PX8p7E&D;0b#m0*}g z&-fSFEFM33+I=XzD7y@{wrZ%?>u{VVu%>FzG<8R8CX>OrGp8xW5eaeyTwQts@k7j{ zR@){}1NY|>c#|(QOG<wB)JfdAJ1a`I_USWb6A7561>3R3cJK|vfx%#~#pLz7JPKC_ z1c(4jy~5?mN#x&u!1d`Ej*##LbK8L|$t?s=ATAI4+UhD6=H?KP2%uw4RjXCZ&)vg? zi<g>gY#UNQ+93;HDt(!Kj@Y#r4h{{0V0QhUdbA`2zfDYR%NG5SRhjO|{(*R8W3g!* z9~;AiR0_A_aZxVUPK=M!B$}{@g=#B;F@v`E#}^QnYakTD!^K6Iv>@X%Gh!%=x<L(h zS~38;dpsyA^+(#<W_VjD;L6mL0AT==vTrzz-^1`r%rcdUdl{q!%Gb{0#*@+EkrAAq zm=FNG^0`MUy@(eje<w*d@3~B5xw~M!*5%UjvJfqgESE^rnnJZ{d^?5N-uzuavXA4z zT}29ItI+YxS@+7kryeQ8;>Jcvl;Kaym3yFcX|k7nSzqT%QK9BVOW(eySDvjzbe+Vh q2gP`{i>-aR{>6WaB6;+u00RIG;|4Ijp<K@Z0000<MNUMnLSTYb|6HU1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fbdac0f6e4557d404a898560117c53f0c53b3c52 GIT binary patch literal 978 zcmV;@11<cCP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#bV)=(RCwC7R!M6VK@_g4uI_OXjg4Y5 zN^nCVC?*k&deMjnFCJaUEqZbJ1G%^a4T*{(2=U^@7(^$6Vh|68fQs8i&BR<hh~#8u z6lG?T*}7}-y_y7^q&p}f1#h~itLA<6zW06ALlYw-F+xZigi97@q#@#5Y26l!#oSeO zb&G=XGnI0a$z&TRM7t|it|U^3MFGOx$>fB~wbDdNNfvV;22)BA;R~VwEGRZ_*w`61 z_b*{`0|v(rrSMDmj)d?RR8bj7Lq$RoMk^<`ZQa&6bHBA=i@E>s>0?(U;skE#-P`x> z<42F0uiv=-vI0b7Adpn1N3(xDGMc!E*X>Qsov+@!?w*{=Ik|kES(X(D^zy}v^<$q$ zTVc}`KpMfMp-!99f`;dL46m6?*2zvz*@kym8;L}e<5X5QHukw`?b>w-QC>qCW13Pr zO`S&SWT>=-?~^A_pI*ykGqzXq`R)7n4*W=^8chO-`uh409UUFd&YwNE|M#z7>rb6N zbF)w=GUzFbI1$OYrOWh5!X=a?LgWo*CedirEAKK#Qpr@~hJ%|3gN7p5x1p~)PoF%s zG?koa*t@TFuxWd9CZErvz+jpwLJe~SF~CR4a7gG}AnZp~RaA{8M%;(-cq_9wJYyAW zeBX;6K614G$g!j2rBaD`UWq{?wbrWK5{*8ZmbU<T(;y@m!$U){yLbB9YnIofmM&X1 zXa2&)O9Rj|IhD&>LI{S!TEcC`C{=i`iT=w+B3KLwsHDHIuYL8JHD7wJUKzaA+j|J< znwhVytsN~C3RVe3<Jh(Y2DEK+1-+3-SA~zHF>G*;1|Gy)Ik$`5mwN_%->2=zPdo=D z4nuN|yDytrP&_a-=Y(?(0<e`ib^gjJ%N5B`F(fCxH|*NI=XuHVDe!Lr7;cq91CSDI z1N59|kg~FETR=4ugraAkorqx$RUP6ZIC;l+w6u(uib#)dp*N5k6bS;Oq_DG&V@nRt zC{`AbOB)}-gbV@d>ggHqfudre2oy^KyOt2(KSeE|3DO!rff6-k(EN}*(`TIy1v31K z@@as+K#I^Cj54Haj^_+;h#!rQv{duGP0*am>>5DkUmNy5Lc?mE&Q50C>gwviMc^-N zw4R~he@8BtL)oN}x_COBZi8^=Eo*^)PW}im0Hg(JY3Oq3!~g&Q07*qoM6N<$g6s9b AlmGw# literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-point_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6e6dff3d7a408e76ad03c5bb10f48de9bbd135c9 GIT binary patch literal 519 zcmV+i0{H!jP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzsYygZRCwCll(9|%K@f&-*8>!+Oe_#3 zhw%Ya3N0yo0PRVPR%+)HVBs@psqqPX1GUpaOsq{5j6MPexZ8Dx{7cp|hj$Q(ag#r{ zaF_XJ{+V5&wRXm_q%)@V|3GfkizTnrdroDu`lHnn!vdaOp8tq-TnQ_*k91&eq3Ds0 z5`)+-V;Mbjq*>ag2P9n~)e`q8^nRKPvl8o&{Lr`Cq;ryhG!zRYQAv)JSzTK{cC@ZK zqCXp_^}E{xSXL2IEF$IvsY?ptf)uG`D$z<+SIXsH!h&EA_4|E-<mr!1(#~6>(d2{x z3X*`>9)I5RiYibw4lcyEw$4x`8w9yRJQoS-Bt7D3S|$hwd9*C+ulLvH7HrBOo<M>@ zY!HtuJs5IH0eAC-!U@^6*KeSd7Z#w4O1jXOibT}y%{K#q&bY}0DZ%#?0#D+%S)6?v zGDD#4o8c$xxXFhIng&Q(qie?4w?Jx`b97COz5yVGplt+BS)h((UGf8qM!T>|Sk_4F z4M>HXpR^*!I_W;724y}9NL%BohP}PxfNX&NO^_Z1VAiJq0|3rJnJL)~h`Rs)002ov JPDHLkV1fbL;NbuO literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_d.png new file mode 100644 index 0000000000000000000000000000000000000000..112d5dba6eea8d76a02c85bdbd73fb53bc7687ea GIT binary patch literal 1176 zcmV;J1ZVq+P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$I!Q!9RCwC7R%=X~RTzHy-9MnEW!wku zYr)OQnAMoDxh*joX1q+Z8O;(C_rpa0`qRv5j4>L2n5j|zcw>o|M6(e5LC}R?#Q|Y3 zP{yV(=%_%;r7dm2ul+9doYS>lf|MV4l9Qg3oW9R{d7k(E9334UegIu$YuKUXa5x}} zBH03DNe)V~d{Uqwsw`<uP0jFlJm5GEJGtWVxRyw!x+o9>Inde)r^^LF5Ox&EG))8o z0gbs&Bl$l-=%O}dSq8IA)Aer<n&)gbTewf(jFWc`^aXAqwP9d6x(;8X2lxAH;cu<l z5{&10Y&(QE&Ij=2M<?L(9%T1uR7Y3O6khxIGC=%k+gFX}PPM?Rxi<Yjw;5yT{`-04 zY6OF~V(izpZMMtHMLd6cKQ4aw7#_NJH@+E|#Z#~J;M}|Ym<=Y11JQKlQ<gt}-}@W8 zclZ8EifjF~4HKqi;-xb!ID7gYNFtBk$uM4gs}JK7D_a9?w)!U*@$6v@KV4gdVHzwV zj^|kJd5&k!Jb8Q%8eKAERl)|z!keEAv3Es*@=;L|Ea@g5J=2Dzw1uhZC@k9oM~q2Q zPLz@)A(=7}SvOEtA~B<JGYdVPCXSigi)dhY9yRrDj4r29TkpZ}=pxgGW#zo3j7=p? zoIO8;>KYegSGiMxTM?lDp(B&Yu*izCZZVRCrAW9Q*?_5th%BdITXrEWzf7*;y$d5S zRT-s(elguZZLJGlH4DQq;2@4zmQ_5WQ%~H5<jfKVz8ygFW(4gI-oa8Ai|gpPFpif$ z=!csamI)CO$u#!)s*qSo;+e<2uu1xD@|M)0ctp)jZhZCjBZRV1RPGYtbPD*odj^Bk zYozcp9zNp3^{Hjp#PdV`225OAK)b&hE~l822Xa!`mSCHGZtSj;(fz{=VPF(c6x_YH z8l^Ii@f*ufN@di0R4jy|sE{2v(cX}&9|58rha>FV7QI9B>v;YB5&RrBAd)DWs!PBT z9bF!sXZ5f*GN^JZn4F8@g{SW<h@FB6gq4&q#aVYHjB_1B(Dv6O99~0nT^Sa_2?WL$ zaO{v5V<c4v_f}%?>O5NNN>Nv%7TB1ZNx2km2{dsdidR1y#<4byw4V@sHD!cxS&Ri^ zIN;rdx!@`?<la#0#KM}6Inw_UNrR;<55nJ`Z=7Qbpm)Bwf`*C`WDN_ylWyJqKqLA{ zx@aq`a4AHjIy<kfuH(4B9_vJ;pI)2>FYpC1=LadrZ!v?GQW=!1A`Cs}Whv2-ZsfXx zPU=FVqUWB$IUK#O4!+7VjD=G8{JUw^nrMqEH0#dJ&RL(fM>~Av=%#~@ijB4sUDt^K z&40|O|N34KNJOftLM7~sgu|E%g@S@YJ{(q}v93!$UeaV)gd{5rFh44&y3@CNR@TmL qYj)8AN$Vq#Er?=}*gE;A00RJNaK9wq<$6j00000<MNUMnLSTZqsVD>h literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0261f254089bbd750408beece2306d8c16625e GIT binary patch literal 1396 zcmV-)1&jKLP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%7D+@wRCwC7R#|ToR}?;XW<2p4ypj+n zCLs=SlRyYdA$=-^f(os=JfM`9_No7&QYFgoXnCmMA(aX$eQ4E1L}DvaHkRzhOTdYp z#m*$&X1g;zcT!gblD02g>1fV)=AQ4|bH4A4$;0vSMgSN<^TO*{6560(nC3uZW1~0J z(C}JNR+g5$xm<2Q05HA&Kme$&zZM`CJD1CP1=BErQo7|~N87$j@l^71?S3x46ySLd zM#d7|vZC5c#qzFT$hWk=y(!suD3bXvFoq!*h5@>!1ILjq4~-YV2fzR0<FCFP__l4Y z|2Df{T`f6=?o1!MadYUxjUP>DX$i+qzTJDjy)}@38N@&khG792hK>f%9qYPOd!aMh zVfohvf4h>+E`&!$Q_Z~x!&dfgw;;d&UeBF#XS(iQzd3e#WOTfLVsh$mEEbzOee&R~ z)`%~^6^sFycp{wwJJ6HKCsw%*bRW1>^RIk&`vZ0z9*rOF><BDcr9i`S6N4J${F$y> zo_fc|y?Y~P6NyAuCX<PN^3gk2dt$q-r0_t%FgSeK>JbC08Cx}fK0V)Y{KOGRr{<%T zs?4dHMi9v)gqR%989MHLv#)8azP`RB+UtOthWPWZe_qi3-p^t)46ar~`hty&k6mWh zL^>akm8z%z)DckBswX|a8dOz{unc*gH<*c{jmk0=E0wa_?sQ;f2xz+MDHe-@qN-%G zHH-`<-I3bUGmGuM-2q51<-ykwg80*=_E6BVh72@Bn~hZvHj0Y<ho6Q&^M(8tshn;X z5{pwMY2D^V-)?tM#4rVl$I@S-_dk-BN?u~KL2g9?BJi+Ls`^ySQqx3&!62EInuqQ_ z{;<v|2HE?XqJcl;0|CfvRhD_Hyc~}NHcZr!Vbu`Gt3XE&w~k)DHhrX6kVLJbs3*>~ zJVGE&TCY0CCsO@$bIJbJ=w7H+WmsQZhj63`mKRslw~j^bXxK}Y=ZUUs#H`U<M|zWO zkoGjX@)tjS_nXDsTFB)fMNuTm_{8eT<-+<whr=!&I@$xz(u-D-vF<3O;?E%56db{- z$|e%V>OD1H(2;KJsOpB0Gw*T3`s1mU<I9E8anVud-q+p&b)pTD$qd+?cJO&Uu#(Fo z5%}!B7V!ZSJ1+>P%_dMB1O~(4sKpApqb=tv{@-t>KG%Rhg+bkoK_4LPU~1wi1kl$N z6?S=?u<$$!UHg5%u%Jw8JjWS?aO}op7#s`6$grKwCZ&#{doy1|J0nf2D+Oq3@Z<QA zU~(c32M%>YVm1YBZDE+6PJ!R=%R1^nVpL`%u!$lS1mqgsOqgQ>k2ULMd%jp~zI*@i z#lF5yac(9Jdl~}xJD`9w5NU0O<?N~@#$aF<NJxZIsf?PVb|9vS`l15QQN%Mh<C*M4 zTg6(l2g6S<`21cxw+V%O0s4BQ@bJ-$)%bcWRiz?ZesQA=y}dCMsV2^*O6M7!QN%jC zveRw0hLz4Vw*{&yvd86eSRzExfTPZa!8UzGR-iSq$MT0qC!yz!2<#38AzzU8&Lqlb zF$1jMW^XJY#2`1WU;jEBZr+x0)SyIBRK8NJ*l_glSP1fO-yN$rf?&8@E*0so*CeyB zkO2WVOB=-vFFt%_qps^jmaBpyt31v(@;^SPge^r?a4LW)+C*Bel!0Ne{|tkYBn_aw z^5=x&^u<jkr8~^19TxTnM4Ld6b;xjTjeR@+5?}yZuRz`u+NJ9N0000<MNUMnLSTY* CHKbes literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-river_n.png new file mode 100644 index 0000000000000000000000000000000000000000..a283365dcdfb374800be92eb977ccc5a271b0b04 GIT binary patch literal 1106 zcmV-Y1g-mtP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#^hrcPRCwCFR&7j^R}?<CeOp?sk*|)7 z0xepxRKZA5MlqTY6-<^aCd)8J<7cKOI=_}V-AtA(+p=Yd3S_bKhd+!8f&-jvA`OBT zidEJ`(4bISDJfz|3uS!t<9&B;bKj*dK-qq9lPC9{^gZ`E&pqedTf}i3%*PPGJdJrH zV#19K?FxR$J$=@Nc*E4zzD_6<!r<WRlRn>6%*e=yA|WB6Gd?cvhCVl^V!Dmhs?@h^ z6rQ7*h)#cT*8Tmr-^B3t;|{CR<#L^`zf=D{@z>|+pK3JAKUtftdpHvj^RGa-Fw)Sv z{E}f?L;aoTEro?edw1@rq(Ap#d`JTQ?x*6$3+IYAFe1@|oxAq*WM;116HYi&jt~V9 zMFcNi_M9U2%bs2`3&TKyWX^ngvheIzUtfuel0a2ug}U@&SxZ~HrRYBwgR${m#=ZlG zpz&Tq1~x_1hs6j|pu->UKcdlU12UNm#>Tv;vZC@TAMmt9SOE{j7eT+n!E7nq3}h}T z|9L%C!}Zwv0(d6ym0mI=+HE#wadZp_1p;{UW{9YbqTfbVG(0@Kdx=^Ny|zA3r=>w4 z5ZDo>3NRUWv!*u5Y&Kh!IQaqrg1Gp2kjvj8`CWh^=D(elXmr%8W*7!~du@0V0eHQm zOKFrLzjn)$qMF*;=K+5}DqAE2QoxF&Bv_l1(?v=CQN_$gl$xsg(%IkNGT?L~mSs7d z@E?g|nc=dk?zeiw^=sEQ<m&a{@pxchU;r{#t%6q$2j_OX_tId5e6CQWyRPM7>-vd_ ziTAwTvFAfWLxT!M;`z2VbFNe(LFw9buv)D^_E?>j1?^@t<mc!Ae)Q16{j^o4Fvdt8 zBNE}pBaunVf4OYv?Ckm|7z{GW$;q&A;R3K&EFhIiAqgLs%QXmMv3R7sq*zN`NkxuR z1S6LBjp|!@$4{MhIvlUI<76{x^-@3xfzfD$l#~?k`FuF}a`?032`H5*yTcmaPr*hk z2HbeZs;X}_<6K2M>~>hTEDfAaCp6ss0}2WXpw(mooh}=inws!6lsEd>v7?W|)c7b7 zH!G3laYs>EX=%x(jT@24)C!t(4bBA{%7ZR@4cKtW6E%s6?*c9s=<e=bbmH?f3aY?I z!A9((Uu$ZvDpe|A@#gkE8?4LI!|l4d(6Pzo@!)a0Lu-%Q10QVNN^B$+i*C~<o}x1s zkqDPH6B`=~e!m~c6CsgEU~+OY)KBB%6Og6L!bAe7uC9VLc%8Of1I_oF6MiVUV5CRK z&iG7B^PUK3)A3!7BGe9F1b<;n&Pfp+LP{i>0||fo3u#bq`7m>EW@(iF=WhbyRsAi% Y01YsnV7aeuxc~qF07*qoM6N<$f?g~VT>t<8 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_d.png new file mode 100644 index 0000000000000000000000000000000000000000..96ecb99f0e945d60b89790965508fe7c5e0b1c19 GIT binary patch literal 915 zcmV;E18n?>P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#HAzH4RCwC7R&7XAQ5b&icDFm1$`qoa zKdYr>er1+eD(WmUBm68vt+c?*qWm2Mf>0zBR4URRiBc&jk)W9d5o)=@&_5OwX=?VP znPYRg54UyBanmVIFS1^C*n8og^PJ~7&-<RsH8wWx0nko{8UYH9;~<mC$lxK!g;BX6 z<nt6HNRX>FnjHuW3xh-=K_J6uG^$PJnRW`qKw@HJ5fT~-p63G!G&eVgo}L~x+dgIT zKR{^EH3dNc1EO**2cbHz6ptEiBYR&Ik~JHV61hbbm{cl-XbNA3KH<{oB3LXYz|DRr zI#nv3*-DOCo==XAqPn6CW|NUU2L*?t{$UFuV-jGs+Sq$MFJ(Iy4diya7eKuEQ6iOb z+5nD7OtFvW<zm~;X#Aa>h1=y~vU8kbRUlDb9-A1)!Q?n-Gm>DoSdm+Lo`6{4u-l1* zZgR#P*#x)h8h(MiY-j{46?oS_gg0+LB823Zi}T?kOr%6E>L@`$*sM{1q8JHE6{4fm zC@(8OdU_&${+h(Ki)Anxels8$kxmi_pbV`R!^0zx5C)ov?I_ICV(>#B&J`ZRtLIO| zwI$N)-05j)*c}tIs<q1sj$q!@Y#Pg{y3{nAHg82o-=N?5dc9s;9qo$Wivwv{=<j`v z3zfxy&-s1(I)azFUSuc5;rfj_q#Vj#zjQPTO3uJEV};plArkTnBQh!iRaGUBI*qtm zQI6IJ_2TNtU!-JZBV<DmS{`?JT<IxjmUtE;|Mzay;PcP`Y8#r@2UJsC3A^2aE7fNq z1j&8QYOx$B@Qe0N=yaVpIiN>$!u|y}ijuCWt`^Upwjn1c70O^G=>R;bn|VvqZorzR z$KrX!i7F*wcC(gT5l&WbUbbt3+`m(Y!u(9cC&s~7ttB3$j6B^T^KdF!+jOwmY_tmJ zfuw8PLCsBfNjDJC)%Bi@MVY*svBv28z_M_a3U-H`br1SJFPF<lr=|>QdK0d6@#()C z7=0=O-Jz8Xr_;HlMfnRSnKLmyPH)*Cyn=i<82(JRcXxHG1wjV6pzw6#MN#R(cG5r{ p4u=Os>_-Z_^u}B+_lbW33;+uBA<fx1L-POt002ovPDHLkV1it3nNk1% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_h.png new file mode 100644 index 0000000000000000000000000000000000000000..0e755687967476163f61d5efc9f154fad05460b9 GIT binary patch literal 1129 zcmV-v1eW`WP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$3rR#lRCwC7Rb6ZpRTMt=&M@8CB`_&5 zY)phMbZbND+Rc(wO$90swj@YM3~Xw^iX=Q36Q9%=>9U)EvFd{{Hek>oNt4F-VhsC0 z+9+*_r4&k2Q6fnx6=jj=&UT^uGk5NI?hJ$NvRm<?H~F&X?wotRIo~<=47of$?gxM< zLW8AQI-*bJmKF8;{n}%zS1$?6o!hsyR4Nsf0W7V0)haL<TM{A>JC(Yn$riW3G|dGM zgMoFiYuBgZrH4LDbTtf}{o#3|Sg0n1u;k##kw+mD95Uw|n8AQjvfv?~M?Nw<xbpq@ zp##h?mfHs{fbgbG1H9w~%Po#1g2N`_oFM=j>Nmt(hbL=;wtf2acOM*$$D&4|V3Poq z^>?~rry|YIWM;Eji4tPTvW$?d%4CAb!i<;+BB9AEleS!q4UI9^-t%Jn8=MuZ38f%% z4u!*`^&3NX|4!=)N={Ud$MktsMu}3E2onOqA#;=4HR2@>f?TY$o~hpaTr((&0{sI+ zu;ZoZ&*^jqqgt>`7J^{k;>PuVs6KWXI8awZa~{UVFU4Kkt9sz#*aVFHI0innyW{V5 z8b*;w66LC@GFA03Ow}?=Vz_WhV<C}c+O1s)=%NSf0<b%}3!0yffS#Gvx)X<9`0LJ} z%c>NG5z$oU;xPmVH8CxQj+IbbS2uC(*C~)B3D&I-!1nf5Nd0khW8&Sn_kZ1cs%EjX zE(8Y|=<h#VQxgCn<vZr`*yzenx{to;?(zPBT@=xPXnRy|s^?_y@yeG7V0!xc{d>fP z%;lLH!;#G6PpmzE>*kH-eXn+)PXD&e)YLS5KQQcntz-AWJumP5c3a!_)WbYZ4^X>U z3=7B1ifGx=I&p6<583Qplu&+vV6YZC_jkaG<%<90iQ}()_4%>-(z7DA;2_Cl^6=U< zYZf0Nh}SziVnQ-^Pwz`ux&rHi=icf{^w0nA#>|Y6&MDYp>*FJdt;M1N2M+E7rAl$D zHSbL<Q9L-4JOfv+{t~%#Vf11s97($_f`f{ICv>lbFM2+0y7JTHmey@8pn6ri`vfRX z3cO1WWM|*LqYbnbUe7muz4&59DhQl7mQ$ljm+i}ApEaEu8r<2|z7?Ko3V|$YLnZum z;Mx>_aZ1?FTQ)xnzyCH9x;Hl`7m5aTFRl3S*+I*#@eAj7`qU~IK09K=nO)X`{uJ%* zsAoN}y<WAD%jG28bD|P4q(HV)Gs_cw)N>*(+7o9X&FAy-TrMZ$Orsc-{MYBfFbrBK z6eOJLI8!;7@sK{7oz;9kU-=Pla+1sEC4?*vKMILWgnra0QkF4@;1JUF>AD_8Xyu7a vf?5;C4AINx|M`q#({P-JI;Pe4j{pMzadFpj|91;c00000NkvXXu0mjf(D)-k literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/add-road-path_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6cecf08e0ce0df12bc719a4fb8492c50e058f09c GIT binary patch literal 715 zcmV;+0yO=JP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!ZAnByRCwCFRXuMLK@gqYJ10Iq6_7v| z#j(%0fg}*D@F5Z<2vQ(K5x`PXP(VjTjc7taLxY4wM}g8)@Cyh95+DlFBN5vNHc&zc z{<z!Yt+RKWEwInF<dH_6-JA2<dGmITd7ejuFir!qfdT2^#niD0Ka_kj8T{Vf4xXE1 ziDkyv1FrESpuF;>P}tQI(EkhoN&NX6fS8R_z=SLA^<0svq$xgRnj^03yx`i9DTPz7 z)$#yb{8lOz@pwq(xp2`95DQbsI450?ebjU<<EvBC^e5%d_E&_vRgdeE;Z8tp#o!4+ zj#JMZMek0W%u=mdB~<rZfefpIi54dQX{_Bn%A_nBOQjkKpiB0K(3{PYU7W>MO;u8j z0@hL9s3M<8B<a%SD>QX{T2^Qs9vyuIHx!5)rJ^YJXw9|@=#56>$#FV2H&2{vE7*Sx zq0C2BT}SGqv>}E~Uno9>e(cnp4-iMTZy+V{$HVdXouz9z65_A8>|&ALzk4eMvVrBT z@8$AFUn2-7U#nIsw6*mUa@QL)O1IK!TDqR2kpl;(k+zNlUG1wPnU}CE5zY-$7&g~& zAA-ueJ&hoobQQgz-0}*=VzB@yxNiWI>sRY9$u8t?Ly=E0IX;Ee$RmK<#Lw!?=`&;) zMnE0M)bJ^kdvbl@;v((YyKg^$@5><)nl$Z!6v(r)vvV{tIT--7g86&WvoojUt2f|r z1=hJo3k&B3+p#8^T7=R>pEM}*2R<Qbgum3K_S*Ic$y}AMsS_HiuPox1K~8r^dK7tc xH9+HCfcWp}etV**j;Um!25C3u_{#euzyOKa=VZ%uh)@6k002ovPDHLkV1m#uNuvM& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_d.png new file mode 100644 index 0000000000000000000000000000000000000000..87e93b120f8e1f06bed3ae2136477d39d1b6b767 GIT binary patch literal 783 zcmV+q1MvKbP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!u}MThRCwCFR!wLVK@|RWW@pyav^A|Q zmfErq@rSljJ$O+N4+?q<f_G2qN$@6kD81=T4*>;1LGfl4Pqtz&Ru5v8dXlP*f3ey> zla{pEBpc^Vn%F3<*}Ch4VUsMG?|n1h``$C+@pvzQ0kv!d5QbrZa}LuqA%#o`DPkr8 zhAe4EquUUPM8FtBP%%3@V^7b{4G^dX?C$A7eM5uJD5%Ki^LR8gWNTW)Flayu+Fu9( z4N6BEG+;TTWr5*8hfw4^(HlH|1AL=EUFRlj-rIqu?lv@UZ}FyPI#n-!*nQzJQtzj* z@L>iY@4d#vtrzHDcvX4-Wbkrrck2ao%bs>Lb+y8Avd9+-`1brWaOoH5ca>0Md!y0^ z*R{D?c)nB48JOCTGmf@>omKC~`W_-Rl~Scq@Q-N;l*5t|t|+4D7BO`HB67JL($i_Y zz48?A?u?@6%>Fgmu1meN8L3c80n3u=DG_cCqxI-c%s)%&qj#JfT-Wu7=^t4uH40%W zj}0Ll36t4Y+=r5rlKBr>W=<>nDGgp{>ic$!Zg5N~4jegK`F#D<NlljS+c)+89!sgE zs_z}gcA%gbQcE+IkJ?wJlP|yE&9#xL-!hqu22;ILl@{|Cbo}lZCT@?Sepd?~obQ8Z zmRMX|QstA^!s1ji%tz3%t0R~k97jj23(*t3aB_|YW6aQi)UilR%n#6&g1LF_G`1d! zBAYE)VhPZyfRQ=)=1ApeJ7{Y_5{*e_p_;Qk)-^o$pFvj&#!`)$nAJL8nVi>NHPpID zESbfOwz;xkya1NQ7H)dBy^_5xI}ziaCrDGxp&o}Tj?jjXwz$6uX4O3`X(i@<%-Lkn zwG3xvpIuLUO(yA)P)ubkm7X6M9)4^KB?_rj$aVHc5OY~Vt=LZi1^|Zm#*a_!SA_ro N002ovPDHLkV1o6bWU2rF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_h.png new file mode 100644 index 0000000000000000000000000000000000000000..80baf68bab2cc64ea6156ff8d9949a8bf117e8aa GIT binary patch literal 976 zcmV;>126oEP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#a!Eu%RCwCFmdk4sQ544SYaUHnn?7tB z7gew*x^h`?p`fKpMJU9bo2q|-=&A@pK|v4{#D$=^=vEQMg@S^(P^gFsl~$rulho8^ zYST$FbMN(>nY5I?k}0GE7xI`nzd7f8=ew7FoS5h(grs35Nb|2EtZ{+BWHOoPP9~d9 zEadZvd_JERN~=UiR~OOJZ#on?*<3auv?fGKN%RiXR>49ZKs8ZH5<*$?p;{5-39S@S z67WoI`M|5&&qgU{Wh-vy+EX2qoqfAAse^lRk)TQw<OzjP$W(33B>RsZda#f$_PS1~ zcXld!EH^nbbo1{0mH)?XT)x^E7@)w0Ol94hQ-}9tI{UkSx~|K8-+w+gIeUOiPn=o) zKl?q~)!y4aFNDy7^Gzj!JceW$WLtkxN;P9lQ_6JL!Q|xXU{f!@jLhZc+C0yrn?1kG z5m;yG`xvlpws40w4MQ42NC-eLUAb_r;#Ij*a9RuRXVSkvWKLSg?I(C2-yqviw1JRx z@CLMTJ!pJEEFlCA0>yU4O09i~x5aONPFE_Gr<^lp+qR_ZN(&32O3Oe|+nrO!xr)VN zo*R$-bKuo+EXw5y$5ud9)cUcJVWz%Rgwbt6DbY-DT`4a0;7CzIwdYkS2#P{A)&Ogn zN`;w9BDb08^|5=S%b(Al7<>Wc*3fW@V<<P63gm@Ciq_MW%X2+@_4mZ#o)6V(g)gf{ ztr>n@GYGIj70xM*hMk`-?ECXIdusJ>j#D&ooKQp%P|LNB1END-&{L$72vW*_oEdO_ z{2emlX6F34;fK=Fi;IgT!*$Etv@FRO#nM67*4tdD-kJ?Idi{Er8FCAyfp}`y>+U0| zH>lLATLrZeZPaEMCPr;G5urI?d3F6P@7#K{BAE2x;N!Ob&QGPKB>@h^IOEzfEy;yY zfhoYaY!*`m<qcdyMe3)$xAn8@hCtIal@O4jQ3I~ZXhUbwwem_CZD1T{!K91^*+EHt zfCYp`;)J_QZI7#Wja(eQ>O%iYrIHDP5teO2@2FywsZf=T39)8_x}ly1?gQCjR+eS@ zhA<RbAZ}GUf`=kY5Um>SDf($ki|s)lDVRDMzmBR0<OPlt;lW@(g+=NKW~(C$o_mWh y*@<{O-t>bR=pi_8qk6}S^Ydv~J9fhVFTen&E>Bv(f+>gq0000<MNUMnLSTZ(&C13A literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-spline_n.png new file mode 100644 index 0000000000000000000000000000000000000000..81a0180b9a87b4c92d9c9743041c8640b4b8b3ab GIT binary patch literal 553 zcmV+^0@nSBP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz%Sl8*RCwBA{Qv(y1HAzg!vKI$OKA#N zh*h|3<!Vrh`vAljfOsPiAE+xYyFf_aod@^Hw-g8*f!L7Y|9=ApM#g#|-R~6;2$$Tp zVLc<U1~C$wWmFUtVUA=6(Y4L3-e=FASqhUkG&EFit*--l>n{-h!{<{{<MbcY(7(;K z)tiwe*xA`nC8T9Y0-4OzPDmh2e*y7#5C#S*2N3gdb91W!1MG>SqGBgdoQ;sV<fLAh zZ~s6Y@N@OjC9i({`q{<E$PfXI0#;De(JIUQhg$xPg@yGwFoD6uS@8uk3l*{*$mjo< znVGbKDf$C66qxXNh+<2D*#?~aPn<ZxU}0%l1ob^5%}S)E&MpCDKCrNu4h`1-P)uD* zn>sojfYf<3VIF9F{zJ~IRIwE3!vbKU*$-qsFf%h(24)*>AQppW7e;8}`bV`m1^Tcb zNEHBaBQQ8ypcb(q<>BAZ#PpwHOHmSxg+)?bMfnmSmjh}B%)-A&`Iv#KsTXMBa;SyO zNQDI6Af-l@;eiI<Payt5C^#vtW!Rw>vO|6M1#0nsTGgkp(&rD9{fAT^l4~i*l5fzg rMliup%hG?uCKjs1>5y*?0t6TUpDLu*4X`N^00000NkvXXu0mjfXv6Nb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_d.png new file mode 100644 index 0000000000000000000000000000000000000000..03fee8c42acde7ba1b49c6e56f00272bd407f9c6 GIT binary patch literal 1077 zcmV-51j_q~P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#*GWV{RCwCFmRo3CRTPH5Gw0UHB$G@N z+a^ugiKzr*rwOGZB@ec=fuaNnme7|Xc&R9YS5y>4tbz)fw*(1NK`Hd16A(&~1gjSG z#Xh7Bxis2NIx&-^?c_SQGc#x0`$R=UYC3Vob=d5uv;V!;`u~3~rMtVk1?UsYPZ2^< z6jW73(=-g-2<e92p-DiuY4{r&cH#5+aJgI*E27cc{#((xJ_+go?hXVf^Lm{d6)Q5C z3<LfBekU!NVX*;a$n$kw#{rckEjHkvVVEW^{^!s|a(Siq^Y{G(zdA)W8KX>DqQ0S? zJ%^7GXl=WDHp{B!1G?|wHfLqCS*EUyaq5FNIrqYG+=j-1!)>&7p5Wf*{p+5ScX@5w z#?Ixqr>=q5dd~3v>2Emj$U#y|gPiSpmKWPw36BpK7F3eXB?Yeu!K)h8{zf`p?ICdR zG2&L7*~w8}dHfIqXL_9r76ep=bQnnp!_YBJL+mDjBd-&=nIYp+Szb=?`N!{(Or=N* zZLS9Y&+L_%OYlVW1`A6|xJy(jN=-bj6v;%AlmO0&cKk!Hg_6R+XD{I`F_^xUrrK9Y zmB&pwop!ue*L0^k1<Ul>r3;*FZ{oMnady;f$0NYPGqaR$^ErgGb9P}fee20biH==D zx3XB`;JIv+SW01Yxk1|#FW=b?Hv-o6#&cZ!?mRQmMRruFlnWM5L}GY+8df^PvE!X= zZG32j<)W;sU#`4B&wueL7tZz)o|+NNgc405WqF7!q^R5K5hZmC%Q4w|^xc(rRLDvi zysB;hbUHLj&)X*$A08rbpAWOHoa-}7MCTQj6ESkhG?kmHFiRBnw?Dr^7D*rhxSB+F zMWo@MzUG~$k8t(!Ac38g>}m94>Mj!TjQH7LanWKnmO~X8w(Ne8=0lx-0g6dMyk2U4 zJ^k_AY5Id7GIx6sx9D5VHZS2Dlg!^tV5&l!WJp*xyJ`*g?(-6OV9QD-(meDP^H^&V z`PWQ)yFOy*N`%r)WmHv`a(N`e$k;sb1&b{eJ6VoPw{g*Qe=QYWH_^#KMuH~=kR*`i z^fhnxPE3R$Ewol1qjtNO>IyfF_09Zn@mE5#Njz>3344*TU&1ujWSPlinFw9y%Fr~= zeDdX;C9|4Ye~)j+qr%24;)w*7urvD)kvBcHjyH>vT9zex>sqtKntUnN_g4i?&tKAO zxsd}Z=FoM9DV0s)vvoWFc~|_qR$n#KFhg_mbACA<t~H!Vjjh(%Mk6vcCAW|g3<g_F v)9e!z_v=DehI9}|TS_tJvXEFEzY8z`@Q+9tX8iDs00000NkvXXu0mjf8cF?x literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_h.png new file mode 100644 index 0000000000000000000000000000000000000000..92bac44dc2e49ff43a4ffbecea28165b1ef0cd8d GIT binary patch literal 1297 zcmV+s1@8KZP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$vq?ljRCwCFR$WXSRTMrmb7z*_Sz%dz zc1hYI2rS?av}qwmMGTr6lSWO`gv2+V^vMT(>WeioCcf!|`e33aYOKblwi-jN4YX;L zMk`fQTIh=Ww7|l$KkN*<!_2+cduA6wgcdOLCMP@D%-rvs^L_U_SGqDd7zTiD5dqlw zl@f8BAYdpI3Y3RJJB}ES#R9QdteY^-0;OeTz^Sw2polX)GaVqD15iqV--X^X5r`*1 zIWR^6BzfmSIRnHaoH1Zjz{~8mAM~C&5&G_%)2%?AB7YGSheH*!54Jsi<%wsWk8K*1 zaX>uj(!jv6hDc=7**XrDqSyY=KY9P1C+A{Q%_>ps)_r>?+B*;SKh)lF#{@=(hd~+` z_~}@EUHzs2MV>CG<2bU=fiyaN)&Jw!uR4ijw`7bQj87&;UwY-}$;bmO#zuSGh~5CD zr>Cd0v^3~4%<mAWHpZAFOA-f3f<Ul*sj>BusJSpDPmGQ2NyL+-7yHf~Ff-|y=9Ya4 zH_=t!GZFLi^MXmyXX-evf;SVA*hdneL{XTgs#Mcd+Sc_#e>|QsnMfp;TT=UazkN1i z7{szH?8?gb;u-OTkOU}_yG=zRcp^6D&ssU%<55B35_mLOPp21Dug@px-Xcd)6rQI? z1cEY7T}Az$b9(ON$4A8GkVq~<Wmyo4bsZM7Sp}P_5<)#l$3Mr0<|}n519=;zUj6w( z=*)@Fy8V7FP!$eC&YFVh*c=qs`^IhCmINTSSNtjkz(S>zVWpzqTLgUL=*x$xk!(~& zhf`?=hGm0gGthO-+28ThMeH8ZhQ@<@E-%C*Y>S+}{pM>O6Jyb~1tY6h26d2C6=vr% z;MFy-Z3>SbeC&EfbyI9vY$!aTcv$8%FC>nRgf@74*i)Z=aDQ}Yuq%~bD5VaCB8`Y- z6-cHns4OoAe?Ujjbfo>z_qe~J$czvLy^hlYX#QBTJg0~b8%Sw-BI^I*qj#T6#^>+5 zx4II1J_6ITDKJe3%*8Bl%Yi++f<Qb9)HLqx!?uDfb5+$SAw*Ov3HDYjHm}}7-`5q& zujgw{fBE@g%gpMv)m0E_sD+{HQCJfE$ua?R!4T)t5ywV@y4u=!&Hm0&Y^$acD$cMY z%fbfM?2ULhPhF>2yDpshx@L6ba(8yo(lkP#vLXn#CU1h7Sp=~mm{|(e5{2F25~z!W zp}DErXJQp9wv|wYD9m-Dbta9RA8qVEasG$1PmldIrRhE&1WNo6{c{2)C+8q%B8OG5 zm|Jp_t*PAw#rNnCpS|UaUF$z2xqPk&Xl=No4IfrZdU+)hsY*<f`#A)IWl&lYfTo6K zxctiy%)~R`^Lim?+b}+U6RIk_*f*G+N!9=U+ep`u7hmmlM0cch#Obz|7iQer_11eI zzP@DJvSFB7E@u&(D)|4yyAN!r@H(_bl`!!>x&ilS9=aB{V1s;JPM0Ty7%3d3PYSNG zsSzl?!z6_fNJ32f-<%3i;Q3y!w@``Rq9R*UwJn>ZDk_Fm#;~f?yd!M{imza%BDQxY zT4!I^k?;a+7)C%BlVPWqX`1eZ8`XO}l}vVvxNA53zXcco7wQ0L;<T8f00000NkvXX Hu0mjfX7OTb literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-texture_n.png new file mode 100644 index 0000000000000000000000000000000000000000..31601728f886bf8e0cb7ae738f0737db7aaf5496 GIT binary patch literal 852 zcmV-a1FQUrP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!_DMuRRCwC#R!vM3K@@)5o!$OWU|VYG z5)*zbEfgpR!$lLKK}?KBNj#W%B3?}N=vh6f(TjIa-1MYe3`XNYjggBnC4i_TQcwy~ z;6k_Co!zZ(nysm%CEHr{pp(30cV?&GeBXQT>u~ve9{%)jz+V{uL4WHYl@`Jsoj<*| z9|*^(ri~MUNX?ROKjHb=OeQn&aei*b4$$J#=O52prRv=cTB=U8uRnzQZgCuU7DH+8 z=y<K``t|2ere^GbRx2;Z;=|l*8W~>XdA?ur`KAOx9OF1gqAL=88t&<p>z&iQ6$XYr zKa)sJzj}FzC;Xkv&JKv8IO=dXK1E`Er>p~(KY0!E?q<%j+UOmKCr#4~Qc3}Vzb!7j zZ^wCxVX}k%ioE5R>nBc+1r5Up@`QlP?FP5U6U2EBhO-=FQjZqUsP@3=azR#6pd}Ch zZ(}2y7Og`S+13Yi=+NOfN;;`iy)zV&A(dK%^u`9D#4jur9d%{e*&VsBD9Rau5clS0 z8nj#vP+}m2sDVJ>c1`9`X`o45#X`AEsn)7f5fBtu<sw)Cv?msueEjHP!je?qEp1hx zXb9&7QM`$+)Ws?kdlOjU<d9)gQ!_YSPEb{K83C^p3yh_^CeXoyLvk*sPGD;tmm~?$ zd0=~MtH4UlW`VVw&+jjMD+t0hYny2S(sH1Cw#@Lc(GgY6zGHwPxedbI5!Qpn+#yR9 zlngTkC!iHByq$jYtOPJ;nTY+IVzZA>jZPw<mjU>Z&6kyxf}H3)tU|%i4Ul|(2(|_x z5{b50Ta6Ny`KoMC)3m#r=?$;P<ADaR7m~>(!1n@JNS4_;N^1gBz>Di^YpH$b$1mI~ zDWH7S-ix^#U_Xp)QIM47ZTQGhklWf|2dmYM&cpKrQmbEKeSIBPP=)2>(#cqS@Wx)g zYsY&9j0Ox>6_&~5RWY)q5|?_fot81J&9&T0xN<TR8>h}%m00Z`fl(>1#0E>V-ghMK epSf&*7hnLtc}Lf!3JP)n0000<MNUMnLSTZOI*VZd literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c32ca6b94db2cf1cb2c3983303ae0e11aa1bc21e GIT binary patch literal 1074 zcmV-21kL-2P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#)Ja4^RCwCFR$E9^Q50P>_mzZ(mQGR7 zkSV=1p`ReCk0`SV%%s8UrxNO;uO5a&5JJ(@9`ukXds;c=)MR7@n)dw3VEVA|BhY+R zRE`$Wyzag2b4JUiX>7D<4~P3P_nx)a-fNw+4P|9zSpb~0%!ol43<d~-08tbnOLD6u zOL-y#j8kNDYU*SpCMLpYG$K~f-Tle@vD@urpd4^UMh4;&5_m?jia;QMy4qSZr^Oh? z8juCsUy>wnP*&1d1NMikC@|tbheYI}qIcxLepJ`gkP0P02TZ0ESXWrFblI}MS2I;r zuvjc5=7|%LoRTu=2eaqQLFTmSfU2UW_cPvfw4=7}KFZ5a<92m5;zy1|`jjaV_vq~G zMAVI(IcpZCW@HAHc<&zW*VW=pdk03vC*aiSa=dJ6!rH<@j4_%1Xg}jn%`S)n7G!5* z=iYr-nv;XBcW=?&)d{=9fyEXJ>=hNzG%dV}qXlKz%QE#vCB+LO-+1-fHB`0^vR)Ea zuUm&>2TRf1+zgM`%Pk_%Hv?G@*(aPWEe0w{B0L@sF4`+;UHS3O=HlXDqOv@^@`hu6 z!)`}QTN^4bUq-`&hd6)s943q#4^7qJ_xnkA+VIL78pjVE#=|F%VYAs_o|Fo&&kM)Z zYskvX<o|1${#$Q{4Em_P9y>}(@a5}Q+Ehj1`t{)Eg@KFBR#e2rrtA9fg~Y%dRaJ2J z^kCzL4Om`K0H4p#%gAiB!&Z#+v~(=V&4n6JdHF-F^Vgss-oHoL(PL<O^%C>v&VzMj z0Y)W_<_cwCtbdNmE9j*|BR79Jd|qGQSu2y^h5&k}`WANW-HmVGJj|$g-O_?H6&H9Z zgQ5R;tg>2>G$xrR!qhE^5-)dDpjD*EYuB%1d$A3R$n>m?T#;0|fd|F2r%$<hH*VU5 zfIk2uv11+;>A%qOMzqXbV#olQG8g77q2Bp8pw@2z0@dlq5=c!;gPTh0YJ5%>QTc%& zl7GV;g%LrWH=S6KpN|<cv*7dl_~vXgWrTvbxHycUyOHJ|&M4V57K^hN;pB-E=ykh^ zsleZj`WS3{;X<R!g*~OEeLd4TyPpxX2(v}7Z^7Pf+qwnkE?o+17Y-V#tbyTjy?~cg z=<#~^25h<zj6y^WsN`8c^_1C&C8EeO@B6P~i!iu=4e09~^-5E<i0|t7$&Q^w8YLT= zrbu(>Aj1^<MET=273m6OA_~`6QIVBacaPi5d=+ju>%UF@MZE3mVvkm0D(Cfn{^D$C ss5eWbL^l5joSh*Sb6G2`yzc@G049cZ*1>gy@Bjb+07*qoM6N<$f_YdA;{X5v literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_h.png new file mode 100644 index 0000000000000000000000000000000000000000..bd8e7acba2656a5b6837bd5d3b9c514c105e4836 GIT binary patch literal 1283 zcmV+e1^oJnP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$rAb6VRCwCFR$WX~M--m>!|uW^y31l0 zY0Hm^1$-zXHcg1jLQ+6XDTz(Z7HuD>iI0sB^|^g9P3mLAQ%y@WsZmH@3gNG5%g=%q zRLc5*LX-lrboVYq_J`ejXQt=eyIU+Q0wy@g$==M)obR0Pd~;@m`}gkU5kkCRcCz<l z1?+~5fG(HIp6hb$Ibv{W$-cC-<dqp`c8AkRIMwzX6mg#Z{nRdVP6(xx@K?Ys14BFj z<%BUxh#>AgC}#xmWX>326yQbn!VO<vzFh71`>O>(h%v^g&1MU_-R`k-pMEm4V^GEk z;t99M#=d;3q+~~%Muvv-Mt#1ci;Ih8ilXd`#-c<JM7A$GJNRB*UH`{tTec10;q)|- z&Y%CRCdc8>)85Vw=I2K$Dvv!r;dYP1{s2U7&5$L#EjwGAd-CKRU?Q`yuwc<@vub1u zA4*6eQfq5#jl+>c$PPdaikN`kg+;}o%Hzi$1OkD??99yJprVKi3-fRE_ViSRlu)qZ zSVe&7!dc(X5L8i!j673K1Db>#Q7lUmgOywqMV65v3unCEvE@+cKvq`PYV+w2eLr5m zc2ZG7C9C1^wAo~$k|gnzZ$q@YkN^~myE`Fr0O#OP4<ex^<olzORlZSQRcb*b678at z5sTRX);FOcC<swk)XSdJj$0k&;ni^Q7nd%5d+_!A@a&@*$4{Ld7vk}_h?z-}$RJa` zer%|bsRj{=!tMdDes{IzuerH*-*0MaFFtf=B^HayBSS+~2VQecVf|E96;xFdfQ=+z zGE>ShQ%dMAzy^Q&y{NOZqb0VsCZn+0+S)FVBv)9!y1M$CNPDXBV8=@h#507n!<r5a z4i)`z=Z*)cvwLc4yPG~}noOvgm`KE>wY9i(y}jM*bmq>TJay_3rJ5*004O>LjHGI6 z2#I4y!Zx@ytP%`bpmq)LTUt<1aJ!+g@uuCrKOTujB><Hm6QQrCrxJqM@Agzrz&zew zB-3KnkWrchHI5}SYKrJ&3n>ls_m_5ecYTDyfXK-F!n|k1H{v1xakIYf?eg+p9XXCz zJf09urVJ*_vfkqvU2n05da?Ji^cC&;x$EcyzaJ87yydB_od#!;s-gjPqQGU@aC~=s zyx{J{M19NIb3RQ={wK*4&+^8Tfp|EpcZ!Yn%fLV>0@gP)w4bQ1ei)0bNsz6CibT3_ z4brM=s`zGMVGybPb8<2t>I<b5m<UIOr}2w;qM<Oh$)=|Kp0XoHdP~a6{y{(uoI?i8 zVzFp8YgQuDVo`xY4OB{VbDa@5GK0S1;nUBat(svW2V|bkuYR@R$Mcd-joI0mJh0}z zzTRe|en`^BIzB&d%w22$;mqe3FMh9Tnt<S&ueYR-INN-eeskr@R|!=WA<GOsI;b7A zvlm{v5Ih{8gamtJRI15jh8FmL;A9&#xK7((jd}RtdhM`=+q?{*-x<E3%L$MX5xB5n z4S*7EaGh>o&orA&8Z_=*H_7Df!Z-QGbIKUs5|kKM7~0FdiPHGhI}$#C!{M;qYPIh9 t!z>bs=nrmG?~Rq^WiQw(d*S~pzyM16Qv++!KIH%a002ovPDHLkV1oG~c8UN1 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/menubar/show-wireframe_n.png new file mode 100644 index 0000000000000000000000000000000000000000..099a142a8d671fcd89adc2463c106adaded260d1 GIT binary patch literal 814 zcmV+}1JV46P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!&`Cr=RCwC#R!e9TQ52mwpL{B%B}8J< zq7_uoT|pO>8bgyRMJR=e;6`2gTM5C1lqk5eVAP#k5f>I0e(=|bZNXJ*{Zx#gpanIR z##U{Eapt`@-dFCFF=U!gqb~Hq<IYU(yz}lo=iUi1O;h^QCQE<8{)7HjQ=NWT*RuAP z`^{}z`>0`@V4Tk~#&me}N>jDuv&W-%%K>`x?(O0=aZ+y~sIYJ})(Y|aIp@9ebMwvl z{CDw~DvFYJxm?Gej*Z@_IVjShi52PG+09LU$mG185HbnR)<}e0kcoV1Xz<=F+?%xm zC9o8vMS!c@V>=Fi`}(CXm&>`uUT^>oneR<cJRh(F+d(T8lGud85}b<K=7v-E&($|H zTnmPp`d_DC2Ebok83=U4Z*DvwDO*S-jN=j;QbRY~pR?2P^&2<EKYn;`#T-Ge7nXXt zEn|YX_Gy|L4K+1yfJ_^Mp-}6pU{Jy=UBxh#1qy&C!6kvn4iL}eLPmtdi7a2S?Jw7) zyZ{!NnVGpYmC1NPJTi!997-!hBz?+cB#+0t8@VSH4F@MMa0yr~KA-RC$b-}+RIw<d z7LIjZ2R}TL8oZ5rTijO;x~(JH2w5Z{s~+&@Wm)Nka?YVb1*ih8bI>&TYD(!vxcA_z z`C_0Y`xLV4$!4<yhOVokd{(@;UQm+l{kpmp2^foK@gNl2iit3LpQ<Iwc|Zuo@AvP4 zQr@tYQnmFgg)G}4%YKMCgE0(Sg%!5TlIj&1TFQjYZX%cqTfrI>B3+eIBQ1D_(YOT< z#PC37rJuG003HKTOxUtx)+9pzLuB-5=;1WPp5~lgZVN~1F^)TJ?8s?X*DkY=JL}qh zZ}-C79?#f|C*xr}5HB`+9XW;RLXaOXmeIfrmNIJ!xv{i*wJo#AosCb&$`w#o)o@_g sYt?*>xD&5xnO~Ed8u@4bZ2uHs0INzb9kHV9+W-In07*qoM6N<$f_j~NumAu6 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_d.png new file mode 100644 index 0000000000000000000000000000000000000000..dc89c1d983dbe5b9e8541ad162e9ff92de1bbb90 GIT binary patch literal 571 zcmV-B0>u4^P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz-AP12RCwBAoHlKm0RscWdLUL{pb}tY zWMp7vWd&ju26i^~2W;%@fh-_PIN8}0l$BK&1O){dn3$Lts0luN_@MCV^OyA?i@=uX z=;|`?^6@dSu&_|oqCbEBFdRO7SOM&OkWXk~2?&79WM^k*09ynKE@CX%dSo?&QIQ$L z-AC6+vW%ITnSrDT*>+_!13Mop!=eLoNcI`g7Cn4<hvE9qM-1G;tPBTVoo0Cd`3-F> zy88SagN~#O11mQ(gEo-9@Zbb#aYd{`FM}{J_FLh~Fg0Qs?Sc-jfrXK*)JL*K8$xzr zh&?{Bm*LaPC)5c#SkVFl|NsAovrqJOFg!eSpF!A4lR;ERmqFJ%m_c6Gn0$*sKvzS9 z;qr|eVD@nZHHNQ1yWcWGEPZtJI>WUi*BJILUdzyb^(JK&!7Kp*%Lli>^eZoGhA%f> zGDsT8Gbp>dF*s+HlNWR=mn;F(D;6(i2#<-ukk<$fWU$gQWKghlrcNB;0!GOt)K7I} z7q0juZ{LK(hmolXgQ~g)MHVqJ(#c0m%rvNjum&9)8{31oZ{H|@YC`lniPGQ#f#=Vj zfv5*89KeRdn|JTm@7}drft{U|fsLI5Y$0+fKp}Vl#DM?-1^~{cd_R)8>SF)^002ov JPDHLkV1iok@wxy2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_h.png new file mode 100644 index 0000000000000000000000000000000000000000..f863eafc802a9ecb2325149f70e4855e39527eed GIT binary patch literal 960 zcmV;x13&zUP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#Vo5|nRCwC7R?SZnK@^{voo-7SWf75T zVk{tv2^da(9>5a^FGiywCVJ6G{0m5oh6@)GZ^n4>;K4+~O_YnC^khtkh~h^eB1Xfa zNLJg0wmUP=_qJ5mSZF;k$?MGS&b;6I`2BXvCtkcrF~)k)9JVpTLmQR`pf{CDISq}C z8-ns}VZm`-w^uR-&X)Rmrj^<dAoA_H^Ns)mOlw`yMjqU4yV!qz;N{e~Q_cRU5r#0v zFcm0f@Wy&JHT&vdA{l36Pe;2d?TuRK8!;sP7(*9pr~l#0jFU6D^d=|Cma^YFKf9kE zZ*{h<NNGVe<s%GX05ekSM>YNVZMv?`aqSu#YhJf7@qWD7&-#{DRb)hafHKl1tm8Tf zv16S*6J$5Xt`D3(aQ;rAP!P*2E0!fKW5;6a9c7d<VPNSXOs&!Yz=m2DD+C3eM<C8Q zB;qy~LNHucC><aW=&GoVIPC0^3xnCLA0rUDrLYp8o;^uz-PPjNZrh$)69_S+#<b`_ zrE&Do;epb6bZl%$N~tj$Gydpd&(im$biB6i-R6eIxsIb<@0!~?z35rOkU*@%1>=?3 z)6|q^v$G!=_C~+DHFzbb4D&O~;j<r$i*2sE(Dr8XWmo@=+e6VNL5bc9>CLM^A`rIP z+>Xmv2W>lM?hjqNl+R>R$=cevrgY0>_nC7ONEU7h0oXjmfryUO+GH3R9^ON9Utiz7 z6Q@qj`2KP%o6X7?vaO~jp?2)u_q=WU&aVxv?Tbn&;dwu73ILyLJ7ya!FN8ok6ztkP zGVn)}L7;dppA-39Ud9t~g-%3k_wm;Ru!3}_*|IDMf`Hc~5<oqa%woEjk*+`nh|p9s z!39vXmas5d9!5+$#F-;OfPf@DM~#JTSv@D|g$HGHG(A1DiRSi>{qwt9TQVxpLa9KY zKvmq1At9upK9FM;x5Mbs5ii{rmH_HH`G!zOxX@*Q08FH0ng>d8B7vZY$r(Bdsarft zfS8?xl6Xm^rD_z(fITP&RNT0#1L`3Zo5jNgz&~d=1r9}Jm+5n;BhaRdW|}gq)a39x zYC$D};x(`O;}_}(<MV!IemcoyvMdjNN6ATP9IAOQMu+eFR0EGnH|%*{FPc*hDB3H| i#s4Jo*PoML0t^6AV6^)o)pNZ70000<MNUMnLSTXc%)RIU literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/move-point_n.png new file mode 100644 index 0000000000000000000000000000000000000000..f3cadb7a1876b9263ab924c46627d1c552bfb556 GIT binary patch literal 560 zcmV-00?+-4P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz(n&-?RCwCFR?AAmKord+X_9F{S}28z zV2b?!MSOumU8pOipWw!|{(*ns%71X#mH0pq)QyT*#D!qRs!(?=D%xjeyi;yqEG<oF z?S;eKnMvo|$GK3#IadZ@sxlxO7?IxTB55c7>_^9vY0g=O5H<1gl*{`e$>pEDXGAI8 zq%^1)hOzx`rJJ0cvw<iT3WXGl()Ed{>8Rhbd-~Wg%#>x(tZ7k^*_|E;EQw<P@k&HG z$-A{j!WNfT`04Rc1l$0i3itsu`r0EAl%_Scqak!$N#Esta<Ff~;s6*zsBwJ6w!?iR zBC5&&6+j8LHAEe-!yHzM#p9VsG`cku4%@!wsGXUnsZ^_?HA-z}e&M?Yez90|uhX1y z0U`fLPo+bWrd?i~TesI&qDNUgI@5K1<LmPy)~<J5GlIEUMR%=IE+4`g6!Yt<jS#XM zFpT@c+v@`ogz&D8V7^O-6%fBMaj)fbk9#P01Bn=Z$7ShzC6deFpb#380^J}Op1IlZ zu0&9>VRUbxWbe}Kh(=ZLL0Re!2IQV|Peg4pQl*R<QYH%e_14Gz%G)g)m!fZdtRq2J yA}Ch@Nr)76*9UzQwaJS2LgaN|d;SOWE5HDt7?~E~=vPz#0000<MNUMnLSTa1a{)&H literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_d.png new file mode 100644 index 0000000000000000000000000000000000000000..18297741a12c7c9b69b09070bd73040e5c6fcb94 GIT binary patch literal 629 zcmV-*0*d{KP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!7fD1xRCwBAoHlKm0RscWdLUL{pb}tY zWMp7vWd&ju26i^~2W;%@fh-_PIN8}0l$BK&1O){dn3$Lts0luN_@MCV^OyA?i@=uX z=;|`?^6@dSu&_|oqCbEBFdRO7SOM&OkWXk~2?&79WM^k*0GkX7E<%=kT{n&4>yE7q zzjoe%i?O+@FmQW#GjIoG;nmB`%*;SALVjI6%JAI76l@8|P^k+C8Dt*b1k)fsNE{>& zQim4+Q{DfomoNXv1)jN^{QtbL?LR&Ml7DKZ^Z)0SBUt56oH+5HV5CUyI887fc;YI- z^xNVBoa(8TmO+*=%ZY*GlBO1c04&b1ran>)votn<vyY#>fFj4~7{u_ta3bw}#3U$z zJ3mok@b~XOpm~rPoLBz6e?dKqK*9C%=P!ouKfW`3`~D4V0VfA1nEt+h9XJzHE$IIJ z`^WJ2?_Y*rzkY*h5FcE6Kr=Pff({g1ARn-?L5mk4|I4CwhQE(q;Vh0=$SWuz!3FXO z$dVt+dl<e>U(O&hZwBRY^y9=<h975-F#NiB30$y($~sV-d<F&=$R{96*tD&2Tf|7H z;+k5?@TF@VhWIlBd!kEkutm%Sst1AWb}&Zw5vT+wDY)6#*dDxn`$hp&6XL6rI4r!u zm{jom*)tIJfQ19taCr0X{rcUzb}O*6vof%;bAT-bH87|I9sqG5K!5=NTQw7s+@e^T P00000NkvXXu0mjf_IeqC literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5e17a9c46bfb935a08a89f29aa9d012ec7fbde02 GIT binary patch literal 1139 zcmV-(1dRKMP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$6-h)vRCwC7R!wMJRTRE|Z{AEYlf29* zjcCo3R2(5VDM$^qBn71l(X`S<v(h3$x4LpC6zRgH;zAcf(~WIGv<gxeg(_4k*!)$9 zm{gP4n2DO?k@?BIdH0^{Id3vL+DsA`xp29>JNKRMo^!tMd&4Kj$GaJ0BRD;_H^awy zNrAN^-QC^ZgZuXF3CbVK%U+>S7*UK_FP+ISqxGHuQS3rt$x{|AGsbLt_?OQw_OH!O z=Qn&m+t^EZ-dxwgCngUJzdW%M7-1*?U^>)H2!7vz)v0g0?|%8oX&F!W>4Uk++`zM! z;}1Mkt^T<bFWkDGTbY^8|9<}Uq2A|DT~0rFq_7nTAP8YZKmZ!&{fA}|1OdLwo;-8Q zTFM-FsZ<i+Cd2gL@SXI~i93aBA0M9i;>s(%0Ha>t(=EUV$YMs?uq_f|{coIqm)2TP z<F#5%;5lvp1c5e2=itfPxW|9K_UY-q#}2)xl(hc>u{41RFc9^Kz_ywJf|SZ-S*~oz z0Kou^#WI6Z%Fs&B9Y=@1mu`H0;j1f$s?{2AexG6!hHBKX)zF3xN55M)=73ZRq?DF( z&g$jFEVUMGkM>TL=4bPjjmlI!9yfHRxldR+vpuyfvr-Chl(J`Ed-bix_D2^+&(Vq% z$KRFd%paNde;9iKD<J3{*L4l&jq(|$=(c(XdJ7?7t4y1R)0wHzN~NlWHO>X+jjRX* zbp%lj8UKGmTw7a<DJdc5y5KktnDy0o{q|sknh_E&hvmiGNJpFW)<|!@3#9iy`0yOk zTdl0DwcT6%Et!ZXbSBePNhXuvPu$Eosg5~}W3|>i#3`htDmLjz!>%)_)k0COlvS}< za)}73R8kmZS83sHzHR@3NsAdF6OnP6DXIR^k%<Irb`6B(fS420i9~|CF*ii!+10N< z8-SqdB@Z8;s8lwhzC%-7Q_slkNQUNtf`oLJSLx_T3F?6!yZ%&lVRq1a=J+UP%fm3_ z!~nWLBQ9Y?rfOmP(gi<_WrL-KtoB!Wbg7uloH+B2`SErxSe(n7An;NH&s|jeA1?$! zATcPHLITmg5%+IXH7$Tj-&{QwEY1)8cYo#Tg*Rn;YR-MQXEN4*Y%;n`5DL-|iC9QQ zCS@COH@(ib1WFuz@kWBN8+v9u8!q3&j8=P0xtMK#>FnE^`m%NV(gInALbaqFh^#4s zvYUdnbsbp)+ZhA1J$>{1k=(Cbo118MyjU#8B9S_dsap|5+CYb>dmvK0IcB6IOx0g6 zu6yn6?X1Q3gc^*_t)<qXpic!hS&;4!0LkG1IS1O5MGzod)5PL(xlF|HsdSfo-ygy0 zwMvP)s)@43U|%gaMx&_;>p03#<FQoHa|_K!e@^}qU;vcf$VJ8rEwBIp002ovPDHLk FV1n*UCj|fi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/rotate-point_n.png new file mode 100644 index 0000000000000000000000000000000000000000..9cdb0b3dfff5c48dcf3c665d9de635a97bacadf4 GIT binary patch literal 563 zcmV-30?hr1P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz)k#D_RCwCFRLw2}VH9n@ek~F46AMcv zHWtJiXn28o0I9@=U_mS;Jb*MM{+7D16du7F5EeEPjeWxo?R3VulY8Z6jGE~r{gRVA zo%woB?%zp!o|o#zF{v({t_j(8A1CwUe%#pJSw<)U1@-wBs2-OOF4}QC*IxXXZr7vA z(a9{rHBci`;0DC+5FWyFk;U`CBHrI85u#g%AgRz@Mn*k*5Zy%F1?F+@t|K8S9rx0* zhzQ9cu{FeX2u&cmVL~S2z~mSiCTao=jxGew5MLp<!go5FIw_OO<X<76_m{vDkPYLt z5riZIZ)$qRk(W=84}On;7Tv+QGRB-U=g)uQWJH%8Cj9fxX{B6Ozk_{Dw6Fs}pHv0? z8B`U>1bb!UFmt(xd;}N;K7l%CT;wHo!!oiRx=An4&t$?;;-&V~5G4|FI1?iJpx&Wu zTjUTJa}w%Ny-g(4;Br|OSrupCo*Nd4F9fmz8|oa#Ve&UF*BG$CUMD-$3Qnp0M_?ae zFXYpka__3*%JOq7m{BCJQyKYLfjp;1S@uS<5qomunrb@4RAonG`;8iurY@TZ*@#{z zWf3u}O5>=WI6?Q0GYn!udh4YJ4JGtz()9TjU;t3}yhUh}w+#RQ002ovPDHLkV1kr= B@}&R( literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4700cd3b3c5fb32b5c76e573bd389cc28364725b GIT binary patch literal 551 zcmV+?0@(eDP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz$w@>(RCwBAoHlKm0RscWdLUL{pb}tY zWMp7vWd&ju26i^~2W;%@fh-_PIN8}0l$BK&1O){dn3$Lts0luN_@MCV^OyA?i@=uX z=;|`?^6@dSu&_|oqCbEBFdRO7SOM&OkWXk~2?&79WM^k*09ynKE?QUu1I*0K3``7k z0u)(v`TTi?#Hc6+JuNMUP=9~?K}fD8hxYAd$jQlN;Ns+DNKH;=aBy;>zC{<#oMFhx z&S5Y%G-Q~+Y#Hr>uD83JK}K4dXiIQfG;!iYIQyNvDu(>xl`9#x?b(AO_D)e1tA3(_ z4x1%<nwoI-rR&!b68|9Tg&y7}E$G0OFd^93EQR?5u9gYaXGB?q>@pA=mrI52L*@QK zd<Ei@oa#WD(0zguTzDLZ0f5!S|ErfT|0foN`1$?!a&h@j3OI4%#D9{KcSTVl!}*Js z7}7G*NiST9DP|7r+sCkD#}0;@x9>3I=jJf@2ZxYu5hDQwOBXL<ShH>egRPY%Lq}H^ zgD$W%APdZzHH)M;A_suuh>eZy!P~cQz%?N)!1HI%K-2>k4q(IK&Aa#OckkM*z|PLf pz{bu2t^`313@U*KKpY4VU;uYFMO3)_Xr%xE002ovPDHLkV1h9D@@N16 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_h.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac38959f4fa75fbab4103ee106c401f792b47bb GIT binary patch literal 1032 zcmV+j1o!)iP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#s!2paRCwC7R!wY7Q53#^Z>FY+PFiK6 z=uT{EugG*SA`+4|eiI7|5g`&ZVf<ByRTJ8dphZZn7>g!NN<`EK!HQvlN|`~@$>?<Y z-n&1}x$>B4Gw5P&ax(Yb`_B2!&v(un`trq#7$Kw`Buc8Yav+038`B<(#iEUi7F7l1 zTQ(cbWHRl75EHF$Xdp_;ssN$wOy)~e7;T7Bid1tUhR|9Q=}W>GtqO<+5X7)RT1$|* z85n*1BzpDo<@o3E@f8ai7ks#J>*npsU<hO&BB{t+0!rPz7rWHgx5IIq={0ND4#(Tt zKIjVRhkyuT2(7fO(f<ZCIyzdP?Cagp)YSB*H<?U<9pd{wD@+v`rIY};F7+Aj5kmwy z{NHu0e;{60SC_re+naJdkGihQrIgGVD7bLYkl~!0nRQ~?C`;`?hKGj}!Tw!$XQK2D z<wDQJJ6HPq2i$3w777JEHC5za2hJS<AOn$@k&%&OGj$L{C<sf=$S*#R^_@rr`(SPD z=tu<nOINOTd7jHDW!C%e*Uxt@-F@uDOdVW^0ez%x4oOL31CY}F;$595%bk%VKiGY^ z^z2jeWP+@SL%aACQ;9WWj2pyYwvT|`NQj_u8@3%h6)4wr?C+}Gy!Q;&qYTKG6z>-9 zJoMWEd=Gz%(=PilSrDirZR-SmB+JPFETYba-Dmw&@^C(%chEa<i}WOur{H~BEKZBb z$w`3>GuZa9BAfP{ftm^2;~oCO5YU`)3q*&8hK@HcTUG`H9zl_(97qpUQZYOWhr{Y@ z*XfPmMEJ<jqql(!B3u)ek$Dk2Bt6gL$Uxk4!L{JRL?RJ?UQJki92>*bDO<c!!82qL zc%vC-nlYx6Jw2^ov)Sd)ppWo86beau4_*tw72_cA9=?y~s2|wIUprLI>^_2CU^@LK zJ^%T$r_FEE>6N*OiRM+Ut5fUNuYd1@_joUa0Ha_S`X=Pyhag1Y8CbxKQW7&^iST=< zs#!qPpvMpHkF=y;zm7L8S^8?rw(Y5wl`Rvl=W`?ivNQxBKrQo2|CdfcUvvcb%Fa?d z>&$E*ij#wqeFqLagr|o{2512!0f!oKB!qCV1<@8Q(<wD#dkBGWYS_?J<q-slB{*U{ zj6~XW5KNf^!XS#j>Hhg1VZ$1e%m2tnYinyOeNQNWFaUx6qd3QO$Z-_p4lw@x!34@9 zv2vKY!CWra4ic>t5>ym2U^He4I$aYw_dh2;1sDM7J&+Bvn0F=s0000<MNUMnLSTZz CirnA; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/scale-point_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1206e08cbf5c512e16490f423b9825f3cd5b8581 GIT binary patch literal 574 zcmV-E0>S->P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz;7LS5RCwC#l)Fj;K@^5FyD>Lnu-0f2 zjMlz@Xk{muT2kZ<v`_@afGCKSwVgq%eFF6rY*magURp^+U6bAMANV(nxtNUsL2%(` zhM9BDH|Ok}5mHLuZ+zk(^!GsNWXhA{@aR|)Lmpay&L7KnnNGIXw?&`-C#M5s3d0%n z3@s~AH=mwC@nkaX*p7mnAd8%;3S>e9kSdSOu|JSqxAQ`X3qo9gpTIigv;!H<3!B?J z4Wp&iwHAk``*IeLSwaFzu&WYa!{2UqK%>*i+d)zjlhPG4am%IbUog}>-@3xlv8+_8 zEr$DhyCwjQ(-yBTBMAQ5Y!{0S`nERKO_ovxchs$Gn-PKrw0RhcvQ%FV$vwYj8|74G z8q|F{mCk_E5a$snr2Yl6Pzph6FU(+m4HBYHh%lpD!v-11)NZCVl1LOV4n34g8QNIT z8}|cT^YQlC+_SnhtfYe)1t2TZk@Lb3b6nR0I$A%uz2vEnlw%Fe8Arx5m}dc*#+XAk zrz}g2Ah&lKtwxxsuXp-K%GkmSun52bbc0OB(LaX_U}|=bHh+gccnC|+%<H2ncqo(q z!C*kEDVx3?RlySLPfd+-ygprCL8eyodan?#Aj!b?cdv7|?^A#Q02pQ5R~7R^%>V!Z M07*qoM6N<$f+r~bmH+?% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_d.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_d.png new file mode 100644 index 0000000000000000000000000000000000000000..9f0c31b5c3490576914f0b13bec8eb80020825b7 GIT binary patch literal 740 zcmV<A0vr8_P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!hDk(0RCwCVmQ8CDQ51&XnR{m@5iDd~ zgg`C2un;Qvg@SZ1B^5XIFQkaBBn!I{+9uXzcj6`#v|{WoSPWuzLQU0`=prtxn^uDw zNM<q<&$)3z(xjPGQt`lHm|-%{`FhWdwz9Gk1CS+;Vo0WG8VtiAm#|E$WLj2SG9+x7 z(Xp}Ph(sdLbsfXBa=9G+@$*-fK?-teVgg4;Mj)jeDyUwsqfjVBRrs9AVL;gUo0erk zfw)|U0r5=;0UiIy{tpn3>0a+=?~6s;zIF59phlz7<^AksK946)pY<M!pp<Rff$i9E zUALpTreVS~jgEJ7xg0KCI1kVBR8U+2UzdR({!z7BB|S7j%S#J*z4@kDM)Hg2&f@OE zqAJ;kkDsuZO2KjJa6MOThk-%-Vki`9WyFw2pT9sfGzhw%A-^^~jpEyPxG|T&NfLgo zR)gny5V~j)0zq6Jc5d`?It7iuCTG*uu1ckXd+8-ynYr4`hJw;Hy<>O=Qt7Mw3nUVA zI5s-^r=FdolzF_S1Yf00Tq-g5M;L;hiNQgf2}4fDVtA0r;7&5B%H`Xc$w?X(2X)HA za3y7|DQJ6t1_Iqrr*R}4#>3TBIJ6*>^Yb)`HtZTrqUW__0CxX)kOuWfKiFp2+}gtR z*;xf)fJ4zYyyi#!9`IFAnYfoBTA+O0d)x$ObYgrQm#3x_fLA_!9;Rg~E%80cSUqcH zB1CV&Y5}0N^>rm$9$7AtX*PxGO0P?p?al83i2;rWfBi^`R?_jzQSTS|zIvnxOS`)} zsto;>EB8S5XmXH!-QMO)NmBFTJKw%%H=b`qEsMlyh17Vqi>-a3{>6Wa5_$Z$00RI} WApY$@T6psS0000<MNUMnLSTZAw^WV* literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_h.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_h.png new file mode 100644 index 0000000000000000000000000000000000000000..b3cfb6aace7b2de4d26fd34b4c46ff90efc38fde GIT binary patch literal 924 zcmV;N17rM&P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#K1oDDRCwC7R!vA$Q53%KzWd%ZPT4C- zrx0_O5TcP*o0=BwTBb2OYm>HV>83v{nhGQgY;S_73j-G|6hSxD#+cC|$eoMOkuXMU z{@%MkopVRU8RrQRbK$~p-+kvh=R4nb-WZ8^JWL3Q!gfe?RvPvMlS)Oy;jnY6p`j`$ zKQ}g<R4Nr^ged1^T^$jeR|N=Tr&8+<Q&JHj1gYji3?`){+~b5&qbeX0KoCO%Ny%YX zuc9K5f{M5x<ps5#Z+%++Lh@U3z%SV6PhXr5Q&a7OZw41iKoo8S5>of5yrB3};y_SL z$Zsvo&o?KRmm+Yd1V|y6(9{(<th8L$rG-L)!pn5`j4@LN0s(2;cF9Z0<Z@e6)7b>a zq@^?{rOGgjy*>eT!dKed^T^iLw(W6G1C5Tnzp;^8Z_@!_H#Rmd_V@RHeEjgy^<SHt z&Aom1-{x|8NX(-FJ0Kt_RisZ4CZH_tBX2M>2?m3%#VrklAP>%+Kn&=qL55a-boAZp zJqqV7SFc5;+b*<kWwTin8011FF*I`oF~CPsb4V(v+cZsCQxlY57Z;uJv9Sp7LCiAM zc%B=))zv-Pb-Q~NX?EQLrKYKrlEs!N^wEyIImp`}m3=ffI~yJy8R@C5J)R2H9y{>- z$-uKo=$ZJF$(o#Vio!a=ED04${H@4-AMwg!NF?G8f`m_>`5t@yYWm&q@Xeu>wT|8A z_4V~jxm?aHfM{&X62O46EGD5h0_iI8k<f;%+@s0yi3np>{>97KwC8z7&z-w7pu{-{ z&M^C;<hg+ZQ!xgLWFP=b>Z!9YrYKe<e8rGjUu(H^`N~Ye^$g(O1Tak1fCeBX*aql1 zQ6Ob$Ss)pU2^eLne0Cy+9;(X6M=*I;FLrjW7V=1sXQDR(wv7}?1V%|=XKmXO49*O! ztU|24p(=cUp2uR79#E9e<$+>BAZx)aXf&AC1ezeM_LFIXIVm*XC(h2SQ@%i&U#^{w z97&`Iy+I9%bRF=V2KMoz_K^~Yjcx@XhSXUD$l-VWzDH<SrPABKx1CTZRPhq{3mdI= yDfriw$z)JAX{2r<olZw#JBOC_pMOsN3NQdaoDUkQt|3SO0000<MNUMnLSTZlXrs3P literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_n.png b/Templates/BaseGame/game/tools/worldEditor/images/road-river/subtract-point_n.png new file mode 100644 index 0000000000000000000000000000000000000000..3180edba495cfece57486bce4bd4dd6aa2eb7e9e GIT binary patch literal 499 zcmV<P0Sx|$P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzl}SWFRCwC#l+7*!Q547LUNfy&+EqhZ zu_6+&VIg$k0eFI9g`Fp;oi|`fJb^dRTCouiAfat|ghA)yI!FF9Ii~tCN@L+9|J<3m zH|KZ0?kHo7H4M|%knA7y_d#~2jrrNRwsCxMUDfIQ#N<?gmaXSz<4}ITe|+ek5j#z) zN6;lwC3Qw(Ka)7;lUi7%l}TqL1sSL>5NA9_a`<I&Y5B-9rfjJ=xs%(Q>wPL4m#d_- znugC2l1B>q1%)IxK1Yz1)ivU%4_(!&dj!eR&IW1wsa|i;ngu9G1LE@dXmo`$(Ymdz z<CMudK`!CXMt~+wk9e9EXkjOJU+vSBZK6zB_)`cl=o^Gg7;-@lcXIjsF_mjKZlKoZ z0^lJN5Bijm>h$-XDHIuW%1tIn5p{RqcogrEarAFcGz3Q8qh6)sCLh3Q1|Tg)HyUGK zfWq(GqbfDJ27m*nuW;-LP)n39{e?xNZInuh(s+1|Kw+}^X*06KP4}U&TjYa)j2PGO p$N%1mi3$bu*Fa_vKxFR%3;=TLk^y8lF4+J8002ovPDHLkV1o6L-WC7= literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..9a3194b242e392bf28b047577690c34e4e075bda GIT binary patch literal 779 zcmV+m1N8ifP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!tw}^dRCwCVRZUD&K@gt4|6j_1G-5Qq zvlTH>;f5C}iJTe(i8rq*o&-;jc=sf2xOwu1ho04FA_px&6Y2pI#N-v)l$6i{2?#Ci zIy3yF0jo6@;v|#V+1)qa?96`q0uvJxR{$V^>Oqr43Isq=RnRmIbY1gx-H0j#5(<U9 za5xOE>p}oGHB(krR=nlqT!MfY#Pd8v*P9O1-rf$yV$qYxp_7~h2nofc5=sZoNq`)P zEX#X9ry>6jP%8BWhDS!=dUrQyhIveTBYiL)i$gk{{(T_ik;rDV;;0mhYvT66fKYXV z)vzpKyRK*n2F2ab8MY4khG`w{c{DuC-yM}?GAWc_fv>HtLvLS%Pb13`=uAAFNl5-3 z)r7ZY4XTwYl*?r(l}fO^v%@EQKQ#q!UcH9a*5IE@H!wKJAgUsRZQFeP9oGQ|$F}V- zn1j}KaE{kd9UC-N;qPMcSYth+tQ5SGmThrx%d)^SE%t33%lm6QZ<%Z<0ZK~2<YMvo zv6xD|-kGMs!41P;CDN%}I^@K5nb#_n3Y2$oPTJYyp%7e3Dt*I15(cEcup-cT^v_~2 z8P&!0t7t0$2^KJc!amtWwMy(i)}OkiyV27Fo10slJ-@)H=WzTazY1fyJY2x$lYP8e z;3Oy++E>LBZEgL4heJb+0ll1@gmCyG_~}Jh-`L=ieQ#-jdF(;0ylZQNT?C@_LZJwm zOcpviF2UI7Xk$Qhbh@Jb{kOpP(=hYtGt7Tmfaf*4H?T}Ec1tif_Z2>TOv2*Q5<Iwn z4`!yP|BkQ+GCP}MB3|q2IwWST-t3KtXX7#9`+nURbqQN5ot>9apZ@z>NA*3GM-^|2 zpUdTt0^&pf^7%X&Uoi|ln#p7m^lzstN~$PIGk=6WYV@Z70{}(!Muxnass#W5002ov JPDHLkV1g%iV{-ri literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..87118eea79a737b2ceb28927608d179cf87201c2 GIT binary patch literal 841 zcmV-P1GfB$P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!>q$gGRCwC7mQ70=Q51$xCKD5*Nn>J4 zAbw0;lr98Uu~vjqD=pQ4-8EU|$G8yu0Tro>1}*5yhMGdbTC>ukT@-3mv^rx=9E<2w zl9)u3^qes_#HsxP=fKOI%*{E^yl3vcjA40s*#iIplmqk$1vSMNi^V`|YpbKNu~8Sw z;o+e}lB57vDwP~grxS40MdjKKas(uU!JvyHf>C`HR45em2N6VX5Y_*qqi8hhnV6XD zh{xlCEXyA4d|O+a*xh|^BNz;>`+Pp-zgw3`BrKs&XmE8keusV=jU3d}n1JIrxbJO( zSYl62mLyTw-rg1_Ctp48?R~H`GBOhLdcD~zLFeb^Kw=<*3=TdG9UL5ZE&w%PHk-j@ zG9l0ikAz$BIh|4;?(FOg^7%aEaygiv|Ijrs;1?p1*Wqdyfv7%L!~_N-VH=(igy)S= zS62r%n@ts6lL$^@eSLjpj;>869w={UXsD~Y9*G!yLDRaO9Z_HqtJSKyAy?#xT1T#E zu26nJp6Atjl%GnaU~caHU7TCEv>4c`t8N{I#bSZl+FEraF%T8xIHNkp@(513RFGPk zPG>B1|Cd##eMc8B->5nv5$Gm5bKy1Jx1!tIb~`2)A(=geALTr@=`km(1*y*I57nTS zmKHIYObRZSOLanWql2LRMkP65vqKKYF{K3N^2w6PWKgHj*Z1(F7DV$L+ptWdqtB-s z8X9Ct+J%#o6SZaEu{}vlsE#j~T*CK5^T)@>u(y{0d^mY{cz8iulT-n*7UC(M9Up%Y z#?81ciW01>e1gr*O?WHI6-XN2#k*tz*4EZwad8QDcfUbrr*CO?_RW}f#j*O8Bd9vu zZg(~uemNbB#XONnq+@IAt1z>(=$X;%`sU^)vA_SxhTrdBZ*Ol`v~^fqI)bi>o}M0= z^e<=H?`p7xa;2l|j=ws(u3(3%a&&a$z|iM|K~V^#@VL&W_V@P#C|y5JehM%EQngX% T2U{m$00000NkvXXu0mjfc;bLr literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setEmpty_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..7f1d4df673e7c44a41ecb95a78e9ea75483e10fd GIT binary patch literal 701 zcmV;u0z&<XP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!Ur9tkRCwC#R6CCnQ5gMZ7~Ua}(D3MJ z4ao*#V?n4aw$jSde_+i10kyE)#9ovA32LX6TaZ{8V$haAga8G>@LFJA`<<J6$v8X1 z5{r$6UvhG1?&CZ6obTSNxvooh5jx#b{3rVNi41>7rBc~{4~N4OZk{rljFW)%myC~$ z59M;X7e><Uc1iOeZxAs-jz2FLkI)y1L<DqQr)srIolb`w$06Ic1!TNoykf%NK}0vK zlO$h2g5=3$k}{bLWwTkz<#JT7*F}qe^ruoOu?ddnSSQ!tZXL&a*kl-nNRmh-C?1dR zRRQUAS}3wCi*~ymMWaz#uh(RnChR?9ykNMu5j8=AWFP`bEEW^VV4Wg@Wgx;_^l6$# z%jFV%&p5|xhRJXa5eXHr3@xCDRGl!Ui0}^*)S4BcFN+jXk*Smg5+Na!03bq5P!H8# zDwPD$Y&H`l2v{k<=Zv^iV@m`QU9MKEW<H-6N&pldhd@QGRue?6R!gV=32FrJX5L}1 zGIvA~1zp(3EdP7I->3O}zAyA*vG`-sWHJ%9VI0Z^g8}UOStu0Fd06x3zM%;BU^<;X z+-|oQy<YEhJRVcASQIsAG#X;gX0xGoyN#<rqtQs!>MdLPQl@de2LTayVdVx=On9G< z?-QQ$fN{^iDXz&^w(2w2?GxAWmuJXgTrt+L4j0x4c{Skcmh1G5+aJ;f?T9DMRcj>< zA8*gmBsn=I3mZo0cuv_)l9=90YaG~}BNsVxks+anIAD2TAM-K<DN{RDiZtIG*Q3fk j_N@z3r*{5VaV@|AzFG;`Z2v7$00000NkvXXu0mjfRoF5X literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1422857fa3a8231af01e8240e1707c7290c85061 GIT binary patch literal 718 zcmV;<0x|uGP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!a7jc#RCwC7l}~6BQ4q$z-E6W!6B<pk zm>S6Tkb?wFL`%g(pn4M;%}ELtHMAA96%jARi)ZyzOTa3$vED;dJgYfq4!N}SAXG>K zfi#Ua2{|NMll9F?LN(F+Vf(@3?aV&jZ)V=?J}wjrc>zS&kfD)yj)S1r!(cE#6b&g+ zGzJ8MNRlMG-ENr8W^ingmdfYz^3F~+LLe0)%QB;jnuF|iI|_w@%#%Tx90r6$dr}Gc zhH((`{_|IG!8OfbI-Tw}s6~nb?Ela-Goh`wUI)HcTioSxnGP4VT1}18X0xef@2V63 zb{v~n!Pq=2TeSktzl2*0A5{Ohy*+Gie^DVOlS!S)CVyX`2bWL5z#xtB_}Up1izTE} zX;`ht;c~guPNJD?@>8ZggYE^qkaP;fN)^E|7aW5}kxr*!u?)cJbpElHZ1U6lo<Rd9 z5pxV?tW+?6^8&KjY;)6gayT5UjP>Tao|@tI#Ch=hKXJl(1Y284IAy1L`0YgBh$Pvt zyPIdT^v`uIAosaP&FAIicYU?;otcZcJ9Fv(pp_R>>YQSApPHV*v!_qs^>{jNzOcB6 z&5cdGdbxxlyQOnRBWH)z`P0X@kOp~72d{RO#l!nIFfuv{gHXoFq2bQW(9m-h;T8XQ z*X8nfPOA;G`ewDet|M)*5>U%dUZ3pgZ02TXu@+tL9z@wtgfw~)Re%DFwt=;P=r0gm zkE+r2Dk`0y!Ctp1AFpRu?m?AGMfiL^_{aTywKfG9i^W=Bgu~%vB9Tz)^}2Ex%H?uO zJRVO9Mxz+WWHJ%D+vzK(Aqaw&w{41zz%Ky?0ECMpm@M1{-~a#s07*qoM6N<$f+s>y AlK=n! literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..44861278545d635e5b6ad3052978be0022939adc GIT binary patch literal 858 zcmV-g1Eu_lP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!{7FPXRCwC7R>4c#U=;tF#kL}9tYx)y z*47GES86vM6`^!)x=Ueo7guo%9mw3gxx?@v^|tLF7&sZ?LGaK^j}AxcMOqZwse|lL ztR89$B3Yf9rA@N$3(JT$YQ^=zkLG*t%lq~BzL%FMR##W+002G+0+hxmhy|SEIA23U z1EJMwOTx0hzfbJ!?D#OA=LwBQ1K@8-@HaPsK}A4{KqzLqGyK7S)_+}~(gBubfkL4W zq;l|@&*uc+|1hLVAVLYHL@XrXa!dhHCX-Pn5{Zg@K9Auz&RHy$Bb`pi6hnk^ol@j_ z@$T(!CZ|Rl+6Vl8N3${hX>ur-OeV`WHa3Wwni>YyoH;l+P_C@3)Y|QK%49N~6s71C zk=+~(MZUYmO=JDt%e%X~>Uca(==J)Oj*bpWjIFJ$5y-DpD%p#ai9{GI1rYJ7|3Rc2 z0QyEQ584{_af_k$XDk*&T}e9}4obQgt&x9yeVvf{&jW@IUtDOaE9fqJV-$zyH1hP} z^48W?B~pt@BzJR5OG}nv7;JNMv+^Pm#DTMCx^8%&C&EKT23_@GI2^lav)L#N!+5za zRH;;~!C*LoXINo2n}3y+mGQF8067%FU~md16NrXJ-utiH&iC4zm&Zn*gr!8VG5{Cl zA)@DlkLFNtdpki)Pk;3Ke1nS)$KCX0$>-)iJ5#At-NeLX@RmU<lvzrI)<V~bL|vV& zNfccA66gfnG4*=%S)>>h^s$E?AAj?bK2Fod`qly>q3gs4R*I#iuTGooHYpdAgWK&+ zS5;L}fk2?`ygmOWgJ|J!xGtN`5?-(OoAjSuX5HQQqoGj93GZ1v8$<aE6wS}iJK-xM z?Q*%&#VSJZcsyh_dq~XA&RXT-m^c{_;DM@1@`tszw<mfP5(M~6QB6%vBrJPD`3lYm z4o<bvSfA|c>m#p9gy8i)j>3^;l8Y&3W@e_WRx2o_kt8X!HwC9L0j)G23>1Y*N8zx~ k7xwn{d=Mr5IQb*M0CUo2MD=99I{*Lx07*qoM6N<$f;-BQK>z>% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/setHeight_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/setHeight_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..83196ea6e634310be7b173809df7bc57b1690e86 GIT binary patch literal 698 zcmV;r0!96aP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!TuDShRCwCFRX=DGQ5es=J8~8gB*G;m zMC}b-f_4gVDd;Ah3RxV*UD4I4orDfz5ZuI}xH%P^>l%=zTTn41ExAi7(U8l~G`YL} zzVe;Cm|iZGHV=OJUS8h&e&7EH-}htlSW0Y8Yi>lE{KYiQSfNn3kL^ymToxBaQmfU* ziD<d5d-)#G8jVJBSP34X*=$~eGys+C0S+^njPqYP%H?u@hQr|wKzkSXK#D<f19FK; z`Yf0D{QN{H^%9kP0LO7QJDpAfKivj8@ZdXO0Y8Kgzkx;e^xQ;mfG&}Ep7#++w2?DY zQ4|Nt8@M8Zkfzh=eGv5klh0=-s#dE+@|O0!Uhj3k-_L?1o=7AtzAI#<x~?Bk7;W3m zM3GJO(O@trAxE$3_4*;aG2-#K4TV7|2Fgwg1O^<zkY6y)=yto>G$J97pklFj5%XxH zQmL#*_0a?Ruq?}LG#c-d$>hsyHoL2;D%HG?e4OH6%~1RtQCQCB^G{%SJ<=nZ3=~cf zy}<c16u=$s848upm{_e=YZ+lKYnpZs#_jRFzwgMSvu2$FlBclzGdJ`VhA!>LkV>V# zLGGhcA46W{b4@Og2_krN7vdJI+hU7?<PWC-Mez<qtE}9YqHs15c>}R$7{*t2pYn4Q z1w`Nz<jr=w{gB_ep+xBN>kw-|N$MF_igzdwzMqiW$kiihPKNdo5q&|GZSy=65=rEO zS2V&_BqA}b>Vm1hz!xbr$20bj;G)qvcl6c^t2)`!AZ)WPxvW(=Vp@n*J{Q4g*09O{ go$Sq+6DI-;0426A2krxD4*&oF07*qoM6N<$f<ZYxAOHXW literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..05678ed315bfc8116c217236a882a1dfb7cf9f3c GIT binary patch literal 476 zcmV<20VDp2P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzen~_@RCwC#l`%`hKoo#ql1r{2=~yUc zxWPrUxalCcSogO62Eo4}F5)0X=qk=4-L1Rv59pd9A*H2+R@B<op6{Img<^}E7Kc7~ zTrPR{zW3f;?rGI(bsYd|r~*<FUDJS>CRmmQoLfHUnIa=dE|(Lo>w<0DprKKk(rUFt zqY>2zq#!~Fr1R84`FtMYI2JlhD3e)0NXRFZP){bafFxoV#s_E~vH<;m5c`R->~>*z z|L}R590zn~IX%$8yMx#B3+P|h$4i1BUM>OOD^G;7X@FkkOpWAZ>Esy9%?<TT+0pw! z{RVm%#0%OPYpV)s?H0zNc&W-T3}qtyTTeb69?5uXTeiiD8he*la*~WX9cA;lf1uVR z`g~eZ-#yQhV;PNt5Z-VfC~~u~qoSL7&1AD!x{}glABUJNLm%IkE?MyFyA7c#$S;t^ z5BoQei7j_7AZ$v06h(lia3+9evq^)`G8tYBf}lpdojM^YV=T=^=%W_j1sDM5zOybN SjHECC0000<MNUMnLSTXgpwANk literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..bd5f71ec3ce0a7a8d301d5745f298e1a1c9b4caa GIT binary patch literal 540 zcmV+%0^|LOP)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzzDYzuRCwClmQhQ>P#A`{9JD!;HB79V zoru&-?+UNVn}YfWqhHg{*^L+dgS-f<K`H23WS~VY%JkDj%{J{EYekXN&GDiS-i>j# z@8Lar`8ceu>mi0=R4j?1Q-Gy${eE9n6h#UI0#qo~YE`OKD(b{wFpy+fX7HVg-g`^L z5kthXEEPutM*0syj^oe=5eRJ%=|4V4V*}Z?%@EN9k^WpCyZH;~rC#S-XJ*KK=I3TX zKnVWii2Nhxm#wR34-p(3B)d1a(eG;>uNTDdVtzW6%6X9jBg5dP?k7i$qhkb_Ose-# zMINsJxY?eVSS|~S0yY5OJUK;e(~Pu=MzWd9r#Qd=PF#xSr&d=?^3M0vvFUVrHy)1< zK~K7l+C?Mse03dlIvvR)2;f^?$;jJVxlvnVLxXQ-v(e5)Dblm757%NMkqPdo`vg=d z6y8$jI_lj&NH33%Auc#m_Jzae{`EC;*bJ|aET7o*Z*Js>oJNE1mP(;+=@LyY%$Lb` zZt7S~({^LA7}eAnhVeNWXx4Btj1A;Y2AcI{sQw>E_yV<BO~OUzzyLr%z~T65mSw3} eR1YVw0t^7D(vs1lW5p=|0000<MNUMnLSTYSSM}Zi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/smoothHeight_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1bec2d18dedbeacb51654d9c89bb076a63f3ff3b GIT binary patch literal 410 zcmV;L0cHM)P)<h;3K|Lk000e1NJLTq000^Q000&U1^@s6-iVB~0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzJV``BRCwC#m9b94Kn#ZC=CUEwJqyar zzzZN{h%y1?4Kiis5r8QVk)>=%EM?79-hf9aZ&9@+hb{eAnQ&364joV|{qkL6`}4(8 zj;yu8DQps)$VSiz`o|!nH%XH829{+xWp||>t?Ig79SS1-XYhd-kg|JV-Fxx{^un4C zL3=>}JhD<0d}b}V;8#%;(<q8kBDzBZa@tQo7e44OP*qh@bcW;$2jIjx<|zu{iDmOV zKab=1));exTxXK^<ed?U%N&-hcMe>jXapGm_SQ7bn%x`gHOsOyhF<b%uX+D*$fuhq z8A#R={0+5$hANgiG5c8Kn;Q!26G-wFauADmq&}&EcWa5;i26WIEeh{C0B06*aa(o5 zP&FC&Kvs1gsNzF6q=r&wmaQ2`IM)6&M&ng}to;Zu0GX$AEJQv5G5`Po07*qoM6N<$ Ef+W|fC;$Ke literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/softCurve_d.PNG b/Templates/BaseGame/game/tools/worldEditor/images/softCurve_d.PNG new file mode 100644 index 0000000000000000000000000000000000000000..89679ff793391f62a87933a23d698bb15d519c5b GIT binary patch literal 599 zcmV-d0;v6oP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz`AI}URCwBAT)JYF0RscWdLUL{paEcF zWMW`qW@2DvVPRlpWqZKN$`Z%|vXGU9RY6`}mO(&30IY~s;PdCt3LidxTo1AoY@vpl z8Ur^s7XvdhGtDgh`}Z%yiBo43z}^D+kk%H00LXk`@G>yL!kyL@!T>NT8JHO83|N2( ziiMCOBLhP)&Cbrjple{jke{E=psl4rerjd{hH7v~XoIwr6oZhEAXY_^GI9(`D!@d> z!^3d($|Z&sOP4Y{disLF*~x({OJBc!L)fQ&K3*vN>e>c|?VC4(*{#hD<oT9V4Iqm` zBcs6<A3Ac3K9+(&WqApMxTF-rteG?EXDJAH`}#3lyL^e^>a`p6vot3w9h{;!ZQepZ zOF;mXg-)M3NncBCY;74H+`Yr_;^iy)S?cEM0;bojT~9wtCB(%Tv~=|t_U+w6KTAQt z!qN&H#H6HRiY*1DVo;tfDK4g;rQpJ%x|-p`yLSvRvGGJDDi&&3YG-4`keQpyFn#i5 zhCn}ma8}aOH(-#Hlf@{zsb(n%#KlB07#kTe%$YNn;rzKX3};TBz*=EI>QpMW7(n%G ze_uD@IAvz0nrDemNNjAZ^tTk$0RHmj3%GemYw+gnTM+et1?bbj&tJZ*KYjMR0t>hm l4s1BlF32AMaUei|0RV+if*(qawi*Bc002ovPDHLkV1fp!1Lpt$ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/softCurve_h.PNG b/Templates/BaseGame/game/tools/worldEditor/images/softCurve_h.PNG new file mode 100644 index 0000000000000000000000000000000000000000..c04786d96dd5b4eb4dd37328a95d3339dd334433 GIT binary patch literal 783 zcmV+q1MvKbP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!u}MThRCwCFmeEhrQ547T@5k?fg78-< zYFaC46N<=k7Pgl3#_Fxvq}9qdG|L`ay;Kj?Ym4e{Kyxc!EXLYoHNmxt%o3~wN+#&U zA4Ze5xVL*ih}7lp2ZOtPfU$e_J?GqW?mf(>34aYj$U@;!>6bvUa|97fwYrMuD=JE_ z7?1PO**ObG2;o&K6~cs+ToiO7(OI4&m>`_Os9X#5AX0=eMhwH0UXW@593~h63|cLE zwDrgh_?B^8vbL_?XYJ~89XA~liV+1Znbq2D+OnC?d6Cbr-#E0jN*$81vcya{yxZw` zZW?|4hB<k{99(Pvde9FPi!C7L?Ys9kH4VE&dD?TS*Yt7B+qT!x<a+qv{%|4Dew_*+ zHbu`9$fo&!*lhQPTa2v^5bk?CP$R}BpR5*3KoF<W6)c9kcW!$W%1x1h{{A+xHaVyc z=&?9my(H&_7@AuSjm||QTGz{0yc9do0X-Jl1*b9|h~@gVt6r$+XG23;DR!U(dK_8y zLTaDZZmaWs`8?T3Z9S6u6w;dsn+j7eq@Xt#{6Z|Yb1D#&i?zZ377~`+b7r$S2;TSE z?OL%mcsb1+9Bewh68mk(_WER{y4pAXVQfE*!YeAhlw2WYqm@ddR+mokh}*-9*`L*6 zv4oOeN$-`*-i-=n<dMzRA--+!-$)DPwB^iTa%qY0>Au(z42I<Ewhcvg_S|_BJg$XY z;DAQckPsFZ=O@O;jW3)|<A~c`>GMx+2n0g%JvyDRa=#$N;XTdVi^Z(Y&I>-heqUtp z>661V)6?4U_is(`e|!6>TdR8&XC85Q&5K>l2M-7o2mR^DpAy!(Ah^tZSiW%uXT}v= z-Wdtfi!m;9r0Fw7LgGh)S16Sw7yOw>z{DazA=&3+3l>V*Cj37F3;@5IPufVQmiGVv N002ovPDHLkV1lCYbzuMi literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/softCurve_n.PNG b/Templates/BaseGame/game/tools/worldEditor/images/softCurve_n.PNG new file mode 100644 index 0000000000000000000000000000000000000000..a1cf84d6a4859d00fb40e5c0803fb7f36ff13113 GIT binary patch literal 483 zcmV<90UZ8`P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzg-Jv~RCwC#R!d3)K@d%MC%7{cvkxjB zKwXFnMcs}!aHn_(KN}TvD|mn?f?0|=fkzNsxe$^YWa38rOt-J38eB-|nVxAuEO=Bi z1O4XJRK0FtjH!eUS^0(dCoMIqqh8L>&e<+YfE(cOIZ66?$)5T>C#7q^7I4Yy-gp!p zAqyX+V${gYOitNqj8O*R4l>~wiVu)ez!a^NLi6S+h)#qMyIAi>6U_zwAW=pm5$P-N zLKqGPr^pXlr+ZuhRaW;JR{H_G0cXHE)J`MUvQ9yskufN9n>yMU9dn6Qh&d(YT{L0L zd#Eh`*2f&Wz;eCOs57YyL3+zM4n~UqnsA9XxU|kC_VwPc5~GV-S=4%wLhU)yJ!bYQ z7%2yNa|;&Pf#Rmip5CNTdxx|QEJ5)j$S;6RV6{x8WER&_szt0jzzWdj?*6g4TuvF+ zQ0&DuCejRRo!Xu<)dDXB_F&N8tWmLhNq$~9sXY{E^LHi^{9HR<S3JqnTsMb5bsPK? ZU;s7&g$mYPKS=-p002ovPDHLkV1lZ?$u0l@ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/new_layer_icon.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/new_layer_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3589b8d2015b484085859ba711ab86c5a41f536c GIT binary patch literal 383 zcmV-_0f7FAP)<h;3K|Lk000e1NJLTq001Tc001Tk1^@s6s6FYf0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzAxT6*RCwBA{Qv(y!(aqV41*MmI&9Qo zqYfK&*r>y31X#$`$jHdZpyie^AIMA~M*X#2GeCCzN5TyFszbaE({|5<F+nCUf*4nK zP6rEs93>>Lm+<c4CAb&?b6A1+9}xeA(*J1{m0(A)a|;ryo*jtUfS4KPNlK#fKNdFJ zP*6y1I{pxhaREO|c^H!wDKIE@7$bUA;<1<zV1`B`BTW-O4B!hb5*<#w!;lg+Beg9a zo*_mI7!9$}5E~7#(GVj7AhkZI4j9QDA^^6>n4uLjJCtT7paoP<{s!WoQ2H;~?J*)+ z9<cTm0}_o35ZxA|hQmM>{UWj{gX|HSJM166CJ_!j|7q<o24XFtYp$gAxY}sHa@1iX d)?okv1_0IeX1Pnug>3);002ovPDHLkV1fohlC=N; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-large.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-large.png new file mode 100644 index 0000000000000000000000000000000000000000..84bc9be6bdd3d121f9eebfca17eeddab3fadae8c GIT binary patch literal 529 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yNtU=qlmzFem6RtIr7}3C<R_&n zc;+Uirv{}arc@T5OtoQPVBGBK;uunK>&@kjyax<8Tn`GQ8kn`8Etq`io$}Ll3m5#p z)V*pJ_l>H1(#sz78-4!q`rS@nmUJe@#s&um1qA^C4h|L;CLouA7$LAqRE-!yQ1g&g zVF<x(MX16MLUJ2S6){36o`Pt^5Q3S9<Sq;$2J`@GaKI2ED$L*^`++quG@0#2G!qZQ Yf*KyqvdpHVz&K{`boFyt=akR{00Mt&HUIzs literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_h.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_h.png new file mode 100644 index 0000000000000000000000000000000000000000..a650fe2d9bb66563dacc30763edf1183f17f2015 GIT binary patch literal 573 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|1|%PcFuVX#k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+81H+!IEGZ*dNXH#)}a8AW9LQ19ZQOeM349$*?2ajAeYV2 z-25f8Mfr7hfm(*vw2qX`dam4hPO)>3o;+&4<7{Vmm2|Jw6h8C&?9+eyH~suLw|Ejy zc+U3dn=eZ;wnhcYgsu%+dHwao3=^p$8#()|>Y&#<Idk>4{QW<zBSLuL|CRs4TzVoq zFGp>@S>vDO_hU)Lt4>K3C%ulvPfp4eUe^~WVBz^V*-}u7q3|8Y6OIZ-OQt>sjutit z1(OFH4F_&NpKs;Fz|wNTAw$6=uh}8v05?NpdG7=khXaBq1Q{3wDvq=%GcYMIFy+r* zA;rPlz`%itk{yoxt9A+c>Q=jOmf!N!{ncTY_q~b_*}v{;)?^KVV-`tv^QV{ZzI#1s zBgeC^Rez^eA1`dGTo|w*L@PCVn(0oKLlQsDcla&8{5o%Y^t~LjEirn{rE=oku31~7 zp1iL=q7<2CH1l7E$)cc@A`B0XCC8n9{`sxf(jo&plPQ1OfB&6lb6)z!HrX{h*LmN} z**5p|)3)oAmKHve{^s0}w)tkmbDo3ec@Ia29zOBur1`t;=BBnWy7O!5nl2q~HIfPq zd#|}YsJZ6+MDB-=CErcuiFLlW<KN5~jRzd1jyId^W0hd|>eD*&xj@`+V0vKiboFyt I=akR{02uxDy8r+H literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_n.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border-new_n.png new file mode 100644 index 0000000000000000000000000000000000000000..ef1fa85c9435806aafcbf53d0a05ea3cbbca2c86 GIT binary patch literal 443 zcmV;s0Yv_ZP)<h;3K|Lk000e1NJLTq002S&002S=1^@s6<5U3X0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzT}ebiRCwC#oV|^MKoEsztnGw=KJFyv zTLJ++&;eyo0tMVPLO@OlWOLs8U9SLP$;oNH3H-BILi_yQn>Pfj%d*%o3^LENc&w_b zq-h$_zVEHB>uZeBZtIr)ru=OGU|)K2{pY)m<0#WKy@kYtuIp@F*LO2>#RgTC8v$P1 zww258U+xTqY|o!Bmkk-XtndepTP*Gv;l7YY0fgs58ZZJNFiRMLSw1+Z0Ynk8#c{;O zBk%|q0T2N3XxA{hMiwdnf@0`G8boY{E+RJ4pnc*INCUHck~ClhW_d3S7*R-r=MfBD zNMkcc7(jeW1CPjbLf7!k2*V>7y7tF#as-TE`s8^8W(g4IlO@uC5io-Gc@(G^!%+ri zxjA%sE+RJ4I50~MA^2lKw&#$}Pe?}x#28;>|Ja0GZIz~zUDNEnTt9n{BZJHDv32cK lrM)K%l!G+B4$}A#U;t0CrlD7+t?d8+002ovPDHLkV1lu1yU+jt literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_d.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_d.png new file mode 100644 index 0000000000000000000000000000000000000000..edb9b29cc5d51df61f65c2b8b2e444df9583cc0a GIT binary patch literal 1049 zcmV+!1m^pRP)<h;3K|Lk000e1NJLTq002S&002S=1^@s6<5U3X0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#yGcYrRCwC#oIywwVHC%|*~ZomLg=B= zi;;nP+Jhc~B1!2KBzn=?(y5Ceh9F+HyFmxRtD>cW2tud!V21)p1hr^gHie}X@gOE% z*4>?*ulJ@m_3h~DTCUCPz6XzQX1;OP?>GPVzW06TXo*BZ4h{~A<>h5jj@HuBB1T3= zgx1s3BWGr2?kRmzY0)+1TvPfl+O#~^G{^6$-mB#~M)TR%dV71rx}t=SqCBkw8TcG3 zC`XgFJj&g2K(HcM5Pvc!hX#W<stC>ztOd>ytOy3dp}`<HG*}S~f>)pCstAoiuyJT` zXmDtp4uV62wZI^Ft>G|@L9ik?M=%Hm!6h~;f<Z6{2EnUOo;5f$ifnq$GE6J3Fq)g2 z1K36)k=2}4kCf%32!QY$nicSXIQmzx?ovb%RWMEfo`4!&L3<h81UKB0sC^ftf_jku z1Ay=vjw=a-&rPb(FF*zRrfGIWqtOT)U!lQgz_V_ewtdA=K~+`NN_%^|1;H?khX5f) z5b*ob;3>C((9c^12%xsMR!mJzt)<iHryCm^-{88RazaXhP<nNQt%9MUp#ZcNguT7J zM+Cu6jE8Q8@XsIsKt1?MYinz0Zf;IM2#ERl`Omw%yN`Evb}$4kQ9ekz8G>aE4Goac z<(;z@H2@$8{$p}-5`SI}4-ZQ?U#qF9xdq+V!LNZ|rCH)c;P~DD(eEIGzXAaR9`EYv zlI!d1zaca=HGQbAuD%W3H_igVvmgM%ON4N6aA0{~U!QDjY@8+)oEd^!K<EVw90%18 zejj`ll41YE#Dvus3<h!C@CI_jGpY}=lnV|iH<%@{`XmVNVM+|a;M?HI_k)Y2r6tkb z-EBh{pxd(qAW5}?XCSC`M7~mYysoY;fU^NUeN+Op-vPuIs1as2H#fr$2(}VDx6z~L z+o7U$LMP+_F*-UbV2TuDV`Bm;#o1Ua_CmSUAO(09LS97xgf?hGSRA1WFdWdJMi9wl z@*Ul8(7@Bw8p6X{etMp~fTgl^zOb-hIp&3}t*ymGBJouzmk$*Hp5FI7Zq^9lvKa>s zVevK`4o6d|)F<qS$K#7iPRMvU6iy5cS~zur4{UF5Ps7ts(}GDTPu-+&FhmLMemfPh z4^!ezx?V{T?7f35L9ln03{gUsqX>Wy7rFm|pP~WV5M(LjgJVJ9`xD6SVy&IYWHhJ| zG;DQUH~oIUtf;7P4!jT$Qh{8^D;J!vXDHO9<1^$bpMS)VfyOV=`1K-<p8^a3Pd(5< TQ6}%H00000NkvXXu0mjfwlmi* literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_h.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_h.png new file mode 100644 index 0000000000000000000000000000000000000000..cbe39f4da53ff4b063f329296e13f6eb31748733 GIT binary patch literal 369 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|1|%PcFuVX#k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+7==Au978H@y}5Hbx7k6Y_2Eu2)r~%7nN3bBnw(x7e691c z$w^J>XxOpG3Y$0VF}Ocbz+dsp>p9kEbn@1`o^`y?=k%r}%B$Z0Ht`L9#nn<d&wh3F z&Rf%USAAabZhzFqLfu*K*UazjeZ1)M*INcW&({?lDz{p;kE=QOag(xE{M*8kyA2OG z+J3B_vxxD?UgawsaRJH}OdJ;(oJ@e!=JU&~lo}K+3h-X6;BhjkVB{$7bz@|4Um@tg z@KAiVasvZUjsN1d3<gFP0S9E_$nAy=a)mde7e3-J1seB&qwmJrbzl5<ym&l6u_Qa< zuJN5`T%YDm<Ib&a+k5J)^{3|V_g5Nl^qSO3Xa3<ZV3<19IZu?62^63Vp00i_>zopr E06SWh#Q*>R literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_n.png b/Templates/BaseGame/game/tools/worldEditor/images/terrainpainter/terrain-painter-border_n.png new file mode 100644 index 0000000000000000000000000000000000000000..f80943fb266e7bb502954a01da0f9fb890b0cf21 GIT binary patch literal 381 zcmV-@0fPRCP)<h;3K|Lk000e1NJLTq002S&002S=1^@s6<5U3X0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzA4x<(RCwC#+`(;wKoEuDU5g4RV@X<~ z0$T7XEl`;caHjxTz+O8CgjP0I<VcpkxmW@$#IN_>&Rix=)3o$`ALe-upICKWhpy{F zl_9ik`_nW{zP_xUSvLP!-r&39lh4Ir7{Z0YvC2yLg>d>rMWmFzGlN4=SEV1`NcJx+ zAT}TZ5DJJ=SW*N8KsY`rLRdZ?z<^*4AY=qZ00D%~@~<Ew5Q;TegEb6YT5Q$;LPk)8 zuw)I^V2%H>hM~*Q#TrFLum)>TMDf%RmK340WDR|U>60RaB@jRW0fgi8g~z8coFIUZ z5n6240AhDld4ED)9gtG`DtYITx3;0Px))c!+v|JZaWCN3?<n2dRpU5bcSuBwG;WJD b&H@Yo=>$v=^bF=?00000NkvXXu0mjf2Sl2` literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_d.png new file mode 100644 index 0000000000000000000000000000000000000000..c63ab98cff91a5eec432cdbe3f9cca49ac4a8c29 GIT binary patch literal 850 zcmV-Y1FigtP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!^hrcPRCwCFR?ACNVHE$}*LRtxBZVPI z4B0A13rYeD>JNy*oe^zq5m7tWt|MARlF+X95^W+ygI<hi1}cH832M~QnQ8KIow<+c zoTDzyxiQm$k8kdsneTUg=kfcFHaa?b4!}6AG+JPqrok`_n5GG3shp*(0h0kEwn}$* zpGJFoJ9J$~tE^NirZ-CEaRwE@OivFI$)tGHD)T%KlM@qZp~Vba4ammlTb2a@<&d@- z@W7O9L&twMi^zorUCXawXz(7cUm290pF>qMMKv_&+t*Vl7AxrL>XiM>t4Y*qE`lIv z_>9n?@k9(RfmCZX+3kpev+INc4h`CW^&%C6h3~naP|C#Fz8<))3(xmZuh*LoI`d%` zL$@9v9*^Nv_eo+M$MD?|EX*y2ay%61lZRtS#N!giNW_+APo<K0{^SLSCW2-&WtQ99 z4mOHqRI4?3u1{h0P$(4PIJ=nr{DtybgYWyz1ARaFQPjjV4FrBbEPdHyu?P&^z^8Xv zDJFj3G|-OD4z#t!aiOmlyVWXO3N42@l}b=p103%-Ml3xksH3X5VbI%mZsO_7$B0HF zux%yz)O3w>yN%vI!i~XeoL1Bc&~Fx~{Fv^>ne4o@#>RR{_T}XqH2!Y?&J@MbwB~`X zudZWj%aMW_9=T7ScO-|4^Gh%Zlp~;&l8d`(po?F>$&C~Qb@ZOkkQAN}Rts}W!dln# zJwZQW5C{dj@?({9R!1@!qrCaTh{2p4M~aExH6n$fKnp7c0try5Y~nx%xM=e13_WuT z`3-=EzPWumnAsiZtWP;*abRmyd$qB#XZ6edmvAPNY3L8!pBzr^Rm0F>Q{lLtFQTuM zg=i!y`(c*NW?5juk>$eCjlF{5KIIl6krM4?Wol?vvi{=LVRNiaa}GPJ?K{B5B?Fht zT1Nw$wz6~OU*$9_Oe4b?AI!`8he2*7pXV({Ow~Z;_vZNY^i<lisJY2wL%h{mF_-0N c4g3*c0CU$?TbVh$g8%>k07*qoM6N<$g6IQ~^#A|> literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_h.png new file mode 100644 index 0000000000000000000000000000000000000000..def996838b4ae3b5ab12d0ec322b7d5b64004d44 GIT binary patch literal 1040 zcmV+r1n>KaP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#vPnciRCwCFR?TlrQ53)TzRwx0zE+Jv zi;6@eBC)X%!ot@=Y=|ZjR?<H}Y{bGsY_MjBAT5cANLZ1O2;w6M!bYc=jxe1vZ`zr8 zANRvKcYGw$&b%(#mz;U;P2N4fd(Qct-#zr@i|1KF$N-o}=Kd;!4Vy}-fu7|(UiXsh zoFkULfAhu)V*?24b#`Znl5);L5hp*E_e?1jk%ALN=RUVe5rTLaDLhC*=Lr<?OiZf= zod*^vG{7<i{0JcugN}a5yQfc_KN!@5Rc^}7ojh~w#>&;bK~hk}V@gR~Qz=CvyFGNb zUkJIXr)Md_`>pGD)>W%hjPtnldgKn^nNmlZyr$^|sUVJG5=IfhyX{!K>{qOGD#g?3 zv|x<3AQD81Y{K*kp3JuI+LD7^mq&92NR{w6Y+7Ha)oMl%hAfIg+H}1T4ff6^(g*h* zXNRubK9b2Sa&6lpQ51JvJa_r%-u*j=x9-?nKtGJhB#{c-aU<Fmc5CSFzOK#=7Z;{d z4&j`WMcrM_{W}l$aHtnrQy;%;`!BsM?BTsfS;knlH1U&!VMKT=NE~xgEKU%^Ffvab zJX=<+){P(tXfr>E$C5_*;^~`y%d)V!0|{y59N8!VkW~z3khibiudP+9W)wxVRkTcr zWRZHE3(K$|*SjM74oE{{&UJkp*Y<1mdZle)n~!8d!?e%j2k}gy`&5ut>TqcAz#C|h zw-?S{+U_{Et|x`|fZA3Dj~~8Cfr(hJIF4x;hSahxHJv&j-d`C^a!FW_AN{()aUD_` z|3Ok|mrP9fYfw)Ope-{Dsg+I#GQCfw*{j3miL6){ces!pKp3)n-?mFrQ#D8sbw0fQ zlmU`S+p@T2ngWuhX7Yh}Ou{a;{wA<@uRbD!H3QdeTw7ew=H?ls^~>j6FQgj_q?o}} zGoP|?%BaK#_;~S$hu>Sa=|?<fNCZr66vj3ef^w**0ERb}LR6$bEtBN(%f5$#9fiCD z#T$L#?koP(VtfY@X(@!(QZxBLyathkOneBV)M0djOA0yCnhGM}NZoo3U$U7L;@N%u z+SOBieSL`!VgR7fmsULpEM%<XI8aV*L~(3@Amd3BgD%gAIx;fyXIxRU7&-K}3ZzdM zl)`956@tS%92!(f-GZv=dOetl*-V1q7+V8dgi)q|G{6nrqZIyA*sIO_H3l=H%99n} zYj1B)h6!exh5{4+O(Opu)%1~0PEHcULm~~AN~Hm?d7JQm2`~V8tqGB>Zxsyy0000< KMNUMnLSTZXtKCol literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/3rd-person-camera_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e45cc4244b8ca1aecbab33e68ccfcdc6d8dbb6d4 GIT binary patch literal 654 zcmV;90&)F`P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!FiAu~RCwC#R=sW$K@gtZy^FDxoUI7U zL~>#${1gPZ6jT%_B@I##bi56Zzzcu`J+I)30xC;%Ao-#T1&NJ~6rb;Bf0#WcU4XlA zArfMx@px~~`}v#wW@ec&2LAToZ~@~#=)VW*6n*hpe>3;}w%<bcrqQ^x8%6N|zds6& zhO^Ut^C=ir#&oaQ@^-u3HL#v>ctU@koo9GwS(-tTreM8rAjW46G`A@5SwII-v<UWt z&k!vZp>~t^_-v^R+ftx-o;yN_QFnc<zjbv3FvRODX}r6>;om+_^}cAgS6f<ZsMl*C zrG!rBvL|DBPAscsYFT>C<(=_l3Q3ZJ%oJpqgu}xLta!_}knLRxGp~~Ap6j{@CTCh{ zWU1hXm!aSSR0a?6of?{`5;T~Mrvprp*Kv|ShM~1#W(*Fdvu`06?104dTR{~r4p;zQ zqI<R3@t@UdOZj;+&Mas+`Z9csYlgr$nW{=iw*5Y}ALFC1`2?7FK!6q`L(?>`M!j2C z{HEhLz4?3rhG+MWk0Wq|@NV?(Y~!*-vxOq96jUqlIp=tm20n(v4_d3Y9OJ7pc8@}< ziLt3NPx}S|Q9%1ADzzat8dhj8Q6yK(gPL5iJ=)wTR50S+q`4@vr&G4@Dy4xgb2%d= zbCEV_xll!7MQ?*A{gl|)u2QG-i>brdYLuYONmM`Wr=^*mk{+5s*spT43ebdr41qFA o6<q}QPnaq=<6swY!~ZV8091YJlgCd|e*gdg07*qoM6N<$f-?~*$N&HU literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_d.png new file mode 100644 index 0000000000000000000000000000000000000000..6500b92c5988182e24f6c40c93fde682353377dd GIT binary patch literal 776 zcmV+j1NZ!iP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!s!2paRCwCNmdj5QK^(`w+kF%<6oEb= z)eQw&1tJmj(36jwiHSY>R~SLP8LA#Uc`(K%9!Suba3RzKrctm>jDmtjqa~?`SSVer zXbY_%ghHJeLL)wEpltajlihh_Kl7X4W0v!HJWT++q{&c09LIs@c?g04NtD8(B)S9& z+@+Odo2>?AWo6K4G*C<N_&51$JmIBK25e|-M2SwvcBGcl=`{NL`ebGnwNPy!75%;_ zieONBq-q1#C26(LpuiG|aRrBR<Wvg|WxoB_#C7)#_#OnZ{r=(m+4rHRFOi%}v3ILn z1$%wn_CVi<#!zLp;C$-^oV(b{HbFHw@Cvhu<c77?HY@Conhj%Z?z5YRj(?26sIS22 z*gMwuKOVr+;u48+9@f1Um`r*sFQ*ZWer9WFGm|k=buc-l<b>w*X;G0DzK4%sHXFdP zJmb-F<RI!B9MBu}uvD7Sa<~Zx>T9#>FJEon`tee=yDvef-;J)jUFhlY5m+rLB&D#~ zt8x6~QT&)+KySB?tjs`BVhB$}F|9=Y3(aXMFKAesUsz!Ma(y{_o<4I5y3!JuDoto{ zHq%5Po6)hkSS*$wl->}3(|Y7mQ9o~6%22JWtRl0Tfzf1O{i#U>j{P<0zSDzd=OG3h zd@{u5rj;4?taH@m2YvDEIf``>%;XqpkbD}Cpt!hbEpL1M#FNaOc=I}lMX~{TUll^Z zP=3(c9i4xSjRZenf&`4gP{FRMnJ;k$q@saz)6=na=&uzhTQEjO(doH|;i0$qo>~j8 zJtTmRop9#N%S~DOyJ)$)cI!&swRcAf{?0&ozVKX)zzc<klK;@cL$#7NoS03poKsuV zio()a5GBbqmrQvB0lzGgyHFy(h&;K%08R~Vm^9Z<0R{m3U(e+ps0;W20000<MNUMn GLSTX>zilM| literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_h.png new file mode 100644 index 0000000000000000000000000000000000000000..0451a0e9c1914d2c35d4be92f471cc9f8c004de9 GIT binary patch literal 891 zcmV->1BCpEP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#9Z5t%RCwCFR!vM3K@@(4wKN#Y0^4>A zD2@D8A%RFWkqgA5iHXO0@#x(~xfn$x@xsx>L=qD90tZ4Q5*vw{#>6OUqC|{D5Gnnm z{MjGDBHdZ%%@%|*HC(zcdAqZ-v-8dS-ZyW@@ZtT03;=HGBIMs<)Q1FBRo&H9)uLpR z^NzR@T@-_%pc_HOGRX=mA$bQyoIo%j3WTUYlr*S@{Bx_S0EmZ?(gp#8PN0Y<Agv5k z2NkL46iZO(MNt&pKoL))7eV!;mk>pZGz+x1tFQCR)J(_r^U%}&p6&Md6Q7)jB}~|M z)YZgXXIf$h7m@-@y*zp&k)@g{*$!7d*WgN*2X50vVE^2#za^FWv!{@hSCr1ncB_AP z-}O5lACF$9Ucz#^U%hLqafekB;M?qH_WXTzp<^?(34%}vM~>RTW|LrRD-EI0BCIU` zbl`ZI_8AWuVIdgC-1+#C5-4EwW+>k6XeLt;c%O}fEL#{82<ST90!__MkgO8eD`jw< zYK0R`^*iHxdb?RWdp_nA=fCu#b>F%HX6Y~tJ{p9P5ibR+hqAI#sBJt3XU}!Q@AVBB z8}>5d!r>^)`$G^32X;Vn(ObZaa{Gfqp@8}N#)g(Vshr&}Ts#lv(h{&$+Q8M`Mtcl& zF=qBI8ja?TG!rc?4k}l@Xoej7C=pLGP&CXl&v0_?mO&xmX2pHV;}->ngRC|Sdk!vy z!Fjw6h98bVTl+}{_~z9+P3_@jc5iSt<fIq*)rv!=4DxK^^;;-58$qTtqDX$s1)#W? zZBKYU_LAiYFP}b6L5eQGQBwueQ`77{QTTJRGDsNLY3_jg0}uA~(SiYfDj18U0@CR; zEHAAwKr|H6-FpL>NcnRjo<Jzk3MbOO{+m77eP1VMMDODl9g)yd>#w9Ht|F5$R_}C7 zUiMt_?ae7aQlgbXH76C>*4SJdqdrQTqx&N`*c>|*iRf@@_y@}VG9)Jz`T5fjjb2sP zNReKhBSkz`k@U<dt4NgGSRx)5OG?an2TUdtfOvRZ8CqRibyL^vg#TB70RV5@4d>$- RVQc^Z002ovPDHLkV1gzNlm`F+ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/camera_n.png new file mode 100644 index 0000000000000000000000000000000000000000..516c0c00ee74300a88ede910f0fa19893010a0d5 GIT binary patch literal 722 zcmV;@0xkWCP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!bV)=(RCwC#md|ezK@`Wo16v?ysja|n zON>-oM602()I%>4<H^LNCle3;5t8ajQw(SS2)*=R!>I|;7^5b}9~W)xp)u8BDT|2F zXj}S&W!IT;AR7c()Mzxk<neaq&Cch&`SBKF7zX~d*zgym1MNWnU8vN|ae90*LCtiv zohi<f>T}Q+8Kq`V)kS@oR-mQKa%(7+lL4h4Q}Ii<csY)s97z%btd?A0T=PI0#C>WW znEPlTiMBJP5-V#vc(wGBH=GXVmF=x<NRl1>1AfS|7v-{sT<#q{6hFn-c!l;kx*S+p zRoN>^s)k9e?WxP<MEdp}D2fkkZ5c3nB7%{lVR-#s3Re&vAH|W8;oA6X$=Z&%Syv;* zk21w{;wn7eU6{E!gH$R_VZ-R{?ZLrA2XN|444WGz%+02GBC1+I=4}pnHEV|MP}Qhb zEN5z$Y+N@=CEo7y_HjFT{wzE_-H-=lL?^~*523+$xUWzs)Ls|!leVr5A*5mdm(Odp zXSUD3?aDBzRCE}+0l)0y?bQ_(;lo3iy^+G$#BmOM{O}p~<<%m$p>XKepidt?f!pJN zLYI*plI1s9xZN)9yE}L9+mWO{FP<-8iv|$fAHd?mVoNA#_7SJWq3PFscefip-(F~% zhT{8=99S>(o(9<JQ(b4)QgIL!$jKPOA~bkxpSEd>udkKPQ59h$YnO2Ab$<T3)je&R z=}>TxnJ#hk&A}0&wz|{FCep?As7}hR3yZF7wG2W`P-<|>Z6YAa1l0wkY=Y|bchm}0 z7y8`_iOH*(8m3J3`m)z9w3!$ptDh3m`3w5rLB9$x04jjeCh&ZY3jhEB07*qoM6N<$ Ef;L}Qz5oCK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..8911e14d72bc9ece05e8ede7ac6693a01dec824e GIT binary patch literal 1222 zcmV;%1UdVOP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$Xh}ptRCwCFR$FLXRTN$K-nn-snPl3T z$qQ|osY%7wG!FGq6-)aeR4F2%2-W_yMFkPXM^q@-f<i$M5rvd0{Rx7g)O-o074u_1 zqA{gOsqKd~rk!b$NoO+C(#+#N+<VU>vGYiTjwf7hX6Br|_FiYLeU58za4-yDn4S>o zFcd|BrfG1wTrhNFN;mX=7Xvo9jZk-Y5Bz>VR8>X2Mm&BaG#^h4GpGReZrOrnpHDKX z*Dy^JKb<)flCrqMdIR#{_`0q`K$+5d0~Uwjc0<MgJaj6qW_bgz9;z82xsZZs*$4*L zR-N<dS4aP?W@fckP|LQ__t2)Y@1xgdFg|_(w{NG(4k{KG7qRQvon`YT&i{dIHb+2Y z8v-jjqbjIPfR<&^+k)fdzq^&Xg|V?o#O7|Gt-S?ZUH2lB$>P$bKWVPXbrif=h(Ro5 zvl*$*FpS!u3@8%MnaJf#`TzX6i@0)S8r~)^Hr~Gh4*f+UGl<3F^1D=|D5ZwL4_hQ> zTC|UVV>?SKu0<_Z`WFFBPFyAm(`ae&qqQxFWHN=BnV2Zv=kt;xjeK%S9#B>CRGPG& zhvVE~ol1ZX&0=ay%5wbo3$W<TMa<64V}SrUnPAY5#>NKfG7D&Ia3e!Bs)Y(wYI~Zh z$vTHwqt7dX+6-#jLJ!k6baD`p=_t<qI)T%tM&Kck4-UVD_ue>y6W@MGhIhg3@!;5J z$Fc9=UVQ(}4{&KNS#LXbS^SDVW&U}xN#f`WKOkM?#IYgl-t!Dms^RHfI}vCJ;Dx=< z;q?RW3bjW+`~*+#cpSd9K5TpHQQFH~9+YA^cko-YIasER_KsHCGrc4(-pdF3HsQ7X zZ{v|Cwj!NQOHdYCdq*1%4}46@Zlx{AYeI^EvogIUnrR^ls$(VdzItgN*Kb&d<3pcI z73p+VylPT{O}=Klf8-sRe{u4%BwePWYL&8?S%DT&Y!|XT^w9u*Jb4Oq9*V$TeEwxg z!q?m+l`@q)UU}(F5mY2HE9*<9T^^qfac9T&?bzJgyF@F|B7t`w#MXy<$v&0@9{X*A ztdkMK+yrh>rCDkYOr=4eIa$szzlRFhsR>_y_XYV>hlhm5b4Eu;t5hnoS4;b-Aa8|m zcr!fYN!xO$6Xghy8-hv!-BKOVA_;JHJsZ1ZeR1uo_LO@i2jHxo0yL}mv29`8q;?oj zByn~6y0GFD6v|c$Q1kEU>4MkWh{&~BX@O<uY9&DKCY&Wd%#^Z3fxPDD6NpCVq(jB# z;sVAX_jRvFAh4#?g-T2*SY4%Ru~NI-+$pNm>unI_vTxUQcA&HKo|4B4ZkP2HbVQ9q zQ)^hp!r5QOP`Onu6+iuVRG!seTeRv{Jv}-sZIF+rV)MQ$cO~{+&3h}^V8zpZrMoY! z7dsU}-G+NAv5*L{xhq}Hw88<r{5<^cp!u7_^{8yY6jf^UCsQ|vM@D`Q>AFF;a2=XU k!!pg@%`rt!|33l@0QbNIW2=sHz5oCK07*qoM6N<$f^Zf@oB#j- literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..cd2cfec3d0d60f1d90060a7c63905800fdce080c GIT binary patch literal 1377 zcmV-n1)lneP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%14%?dRCwCFR^4kHRTMunb7#M1H*WSL zYZ{Hj#9|Q<U;GRfM0`;2)fDNIAR;A(VCzRIrYNn|C@NL)K|%1LKIn6c4+Z@LDz^C| zX~jsJEW63>CcE3r$Gz8c?ww6`)6|DZI?H8dXYRef^E>Bv&bjQ5OPB5jfDs&N*!!!B z;~XQvLZOh(6$*QfSS}XR#bR-U2`SRu*(^vN>^UgmEG;jknUnyWbC7qyw*m*@A*ci) zIDld9J*X5wcuWcbf+M^sIwpMi@!9wPQxUZq@PI=$o4M_r(PKwWc7O^AgvSJdB2&?c z2^?Px4h@vHZ?wErw5O(KlfLIM(=?&cZ2Hd}e7d@Q-qnS_@{LB*G%ZtFI1lGa4UC|O zjZAsR0(wVr4(NRt?0UU!UAa8%)T*^;E}w&3t{+_2g@yU6b`Z3!H$tOvZKKg(He(_Z zjc~kg?yZ;-E29M{HA)Z!5cs}&o|&F=Zd_lAJ5ItF92`(~EZta!m6a9s-Su1pFZFC1 zh6H@(xc=tGhAAQuK(+$es-1;)uT&FSiwG30G|2jY7OzL==NBB$^{h;$8;D`Gwg%-= z2`r02GU?!kmg{>y>d*l7LsZMOWWa^;Aa5I5v`R>_gDC-WE(|hcc4jWc1y}Zz%4Jwv zTf^@H(%or@$74`KF~y=WvEgc_6k`ixC@fm<gXjAOL${K6T4_mmSRA-|DK|j{_<?T( zSS;xN`B#%99=QDH6#RDacZkKJ@W$xt@ZrR1nEc`lSj@s#k?6Nye^)(p_~ovjfBL1y zm@NavE-<uE_0Va<!6anmitq!189rJ_?!A9-VBNNv{Nei_8_zxeEHGxl@WEll^u?E6 zfaAwbP}&%0PJLQ`YWNAKE0r`KfATR$t@lwA_|5k{gIucj7K*8f?!$*Izw7kT?#N^_ z9<y!Pva(bv!~O>!fOp?I4v#$guu?DS@!G;Qr>D?ke=u>nk?+adYA^Dh+J}T1#+2?j zw2<0jD!85~xOTg8c@U_$#$z$)>+6M&K0Bo>c3lsJW74ygOeSIc=va#8lMy5;ba97! zWmq6k$Dzu?)Iots1i*ddW}L_09*h2Z;UXYwWzdn=-lVJm$8l6K4Z=J>I`$5V$-q=v zjR}<?xVzMVT!cjMzj5LG`HBAi``SvCAQ2g7C%-u{Ff>r6|0MO<>AAZwZ&ild2u_<~ zDya?ah)wyaDg%KKT30G%yz=UyPf%q%kx2M9v(<;i#lNxQZk9nkBxn=K4Tg3)GB@PV z{-GK&4G7Ffkx*_@ibPS(1SrEQglpu!eZ6&6XTz%#gNg&ums^dESP_U4m?}@YY9Kp# z;GqN6X1&4I*XyiQDmfT`gJP?Gcu%I1?mhSRHDa+CuT-ijEEKY(+!hAmg{^H{wU-?e zJ6dh6wic<ZRuY-+46LlKDkG6?(2ML+T`6C0tk-KH>MGR2P_3<%9@{V8rr!3e*=XAF zL>xTN1FEOlT(;3u$lnAVlWpsz&IFY*25fbiu*cofaB=eVm6`lbeX`XQsP>gh<s^-r zo3>PH-D16224zP~Ti5t|qbIS`m>U}|!*Xcu_M{LBsx77n3R2t}(>78_Mlj3T^$~^E zYtM%mGHh+P10@SlwV2nI*exiX7}~|ES{ogA@Klgi)n!MfpxV{{J(LkJo+Q-qiqw0q jTB(fSxML^$e+3u-r3uRYhYazi00000NkvXXu0mjfN>P~& literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/datablock-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..44d08018aaf6bfcf084d7f7cf685a29f0363206f GIT binary patch literal 1056 zcmV+*1mF9KP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#!bwCyRCwC#R!eUaR}?;XW}I;{cH*(e zv59a9B4Wd18_7rki4CxUbzw!Tg1RYT*P;R~DB%$humZ7yC9r@E3j{(4@eg<`;t)HK z*3J_qc2duibFOD1W5w=(C~P>==+5Jw*LS{i?l9MN;cJTk-$1@Q{a;0D_F?qmrIrlS z-|Jx8HmGs6>7J`sF0=aQTz;apQ`>eRlUe(s@qMW@t>kiJY0EUjf*?Y<QmzaP4CEX4 zy?OI?lV_iTcf56Vr~Q5FX<-Ks|H3ozWpS|_%8iY!(RDqlX&S^62{5Wv7$46mrfD%j zU>_mlIOGRp&!Itiw1St_GPQt=aaeL=-KAnlRU(Smw;qGS-YypZf%&;Pu3(xbL#Zj_ zO1HLU(PM}{`!@fprUGU8_taFhP{^kZ!w9Kr91>arbp1Wd&dx$el0b<>Kwv_-<`l@A zON%H9ZXHT22$5)$U}2<_g+dQ1i)3*Q9qEfYIA|;uQz6{mj%0cVp|Ff7R|Q56QOKd| zieTAR)9z^Mlp;lV#S5e7pg27Z&tJTRXHTDk91g?bLqEgF$Qd{@G7MNuRhHYju3o!7 zabW)snOnE-<V8{P`5*-C)0IqndwZuuNpx=9xLMk{V>^flX*RnJx;i^y*X})V{N$hD zIu1KOdhzYx;9xQmQKW1(n?hZNko)#o;Nd{*>x#w7k|epV<8sB>nHku)u^;|8egd{` z+05BXlr7{7Ym%DQF+6;>)Sc86BB96Lz%{M)sbwQcYl>JOsu{0iI+cRcr~U#uov@Zj zByO`U8d2cr(62qbM<*w{sfSl{*ep}t%#OPfDGUuA>3sC~31=+A2}g#G^43rk1^o6R z`R(^({QbhhJJr))b*iz(^Av{z1tRX*yU$pg>3!umHp2+m#Ov`4)~bVyBIFbm7zV<N zYp5+m4N5)3(LtMbG#VA}-@B`MgJGEf^T%NGOdZ$1cX8S{>(=#6p|fg@)h1pB2!a4@ zvdkUUWo)NX=~BIZ(3+ytm6~!TDI}R2`uoS1Dof$YQbk#qpVx4c=+~WygK#f)r_#l6 zST=Ep^mrpMP@j?$f|OudL7$_Cf&)H^Crna}#{nI4LSeT5CX>mcqC_g(0KW4wg-^-U zZJ<4kTduskwA|5-EH({-SF~fX*i1YTpR4V0{JlQZ!HDF`k3Su1dY<~m8EgFy@Z{lx z7!e4sYCVyp77EP!F1OnEMgJV?ne0XTx#Bd!ECo<7noX8~;x@84NT&9e{xM_U;t&6W a00RIaDd90rv0JnN0000<MNUMnLSTZKmH9LP literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui.png new file mode 100644 index 0000000000000000000000000000000000000000..9971dc15056e75e11e7306cb30b7d6ba3860f35a GIT binary patch literal 350 zcmV-k0iphhP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz07*naRCwBA{Qv(y1HAzg!vKJhV<`(R z2^9rJs#|vF!F~Le`iF+$Oko>WuVN%t4M{%z{||4fW(VSb#Haz{zfhX6rGNkZ#b+Q7 z5dR@Y4G@1L+R}f2{}J*9QEIRyR@^@Q`-fn{AVv*Iar);EVFPz<-NJzsl<X8+`WJuD zp~M6<?y$NYJw6He^fy7<tQ{PG;F3FfU_S>^>Lu2ve~F0Gzx0n&0&2*~GK76WKn>|x z<_{5;{-Jj84oI9*A$W(1PyZ1v9EedvlBIWUTxW!)K%7M!Pz`m9K@y8N>J)>pXoF^M wCL9ieMJh3BNb)H$5l2$wGLD$WHb8&@0EfGd&S2LJKL7v#07*qoM6N<$f)3(~S^xk5 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_d.png new file mode 100644 index 0000000000000000000000000000000000000000..376b3f9d013dca653f28deef72985dcd34b2b286 GIT binary patch literal 433 zcmV;i0Z#sjP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzQ%OWYRCwClmcdHIFc5~PNhZaE3w;2g zu<k+F1z)B38a|Hn><dUAV8wj^uPz=0sR-*u+f|RIsY$@Y3T>xLQ_vqm4;kjmKhvfY zq-lBz0GU}acu)`oz&QsY1W1%QN=$?W9)vQUPLCmqB4CVxFBQdG{92Tmg*xDTv4F$L z#GS~Olu~fLT*l6pb?6&tW%DB<IH)bDZ{Uw)7y^Tlg~nW~z4`3CaqsTof*iB+HruMU zpGy<YuKV}4b+;xYtOtiG0#pqPtqBS1p`oC=6RsaG$9KBdCDHzle03%`IXY|HyMMm= zBR!=!rNs1tYJv+rxYLSI-s(Smr?svKJ*@`rDdBRU#Plq$CX{!N3!n4m|M=7Pwle`G z=C_#O<BTagP&0>)4-I9Qm)p{fhrac)+1N)eP|D=}<1<^WZem1frf>u<xSeKvaC5Vg bZvh4X!8m=ey(%7M00000NkvXXu0mjfyCc0c literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/gui_h.png new file mode 100644 index 0000000000000000000000000000000000000000..96e58de3f2c892e9dca83d7875e00afc4dde3873 GIT binary patch literal 549 zcmV+=0^0qFP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz$4Nv%RCwCFmc35HKn%xyRkkicMG6Cb zuWl@eg;xNTN<0A)5<)C&9m|uHcPLVc#E_xgVjDZTs!A@GUO>rJH`V7~{{E89=X!ks z05iEU^nWtB%SeGqlEmjp(mU}dP2)68XAv>QXOjsq=DkCev;E%25i<ZH0uK;dlB+y{ zG7uqv8TAik0_8<a1cZVgQN`fqYCiWpetlmoLTxHgVo+X03KgY9?pOIvp69_fE?A(# z1|;_}li%UNKE%fHE5A~y6q2d`$>?>+h`}~hpjE1IMhS&6rWOW&3!!xwVpA2uMP8+l z%J3TJ(P@WIFZYX1&>|%oUI~%rhMk1_Lq{#SJl!@}D$S8#RFg_AN)OQ6>pO4DJik4< zEyGqRk}5?&r}|EZ7dz&(e1o=-G^bsZ+Wri&sTLC0LgKPz>eO}>6l_zcDzC_?(xFyL zttya$ZQP;5$1&Ixdw(HeuubC_)Yubq@S`$c9Bk8+>#~p<Dy_}oaUx;o>a~T$jm;i1 z9R4yulxIFxtB1?!6oxuqn~mm_|25XSkHN#{Fd($p9C%=*#g3#4@NTz@$K!GD!2Nz- nEZmyYWtL?#xr1%^zXA*ZGSIKhsI=oa00000NkvXXu0mjfuS)bK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..e850a5873170a3f59857252d0f9028adf91b994d GIT binary patch literal 1083 zcmV-B1jPG^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#-AP12RCwCFmThQTRUF2j<mTRM(wH^H zCbsKp(p7My>E`@Un9PYEwo+wl6x<6-(TQ{p*@r?E@wNLP3?@h&ohbXViW3#uT39Em z)=l<dY_PG?RomHSwN27COV%V!?^|yC-&3YmSTj@7KOAn}IKMpq^PJ}#Q%6U~W&m9@ zJSae!OeSy~2aCl5ffs_j!22x*&?*X^_3NwQbUI-+n^CBW#qM~bv3QpOWxyJr4-Y#W z>>m}XG)+VQ`STto%TQQopi##BJkNtcjg}S~c&`Legc++`Jc;{X^3q)=vG8LbwEj_y z)_E}S#8wp9?Wia#gRiz08#Zpt?Plug11VJ}h7sEP9HK4lkS1mzX8`xMr(rL$<Hg2C zILHq@y}jr>eHzKcT+aD&LZ#pkCSKkP4OzH*PeIYM5CU8A_@VcZnoq&w^`gF^0f(EL zq3b$2JHN^a&I!H!)|=2POHh9D3^bZFHIhK_!Ir<jpNWK_YZ>r@2*2MCtJR9`?w<Ui z;creLaV3IB-#tnyvIw0y1t|-Rf6|7bSD%MVNg^7(14)sgs0t)m!86;pV=^>_;hQ(_ zzjc;{o;wR{abfJscFgq+E?U0Y0X3V2%~u1HWP_H;Kv5JZvJ6?(45z^Bslwom8}~KV zvPx%v{R6S<5u_6-tZRD%=2{mVJDx@DiSJO|at!9ua+WfZB$HYNvMiBW87`L_li_fF zN*73xw|N&fy?va$5B8lUBk4<iqmVk;XfK1(pr_Sl#e6E2A2glSX?y$wF<z?~{XcoC zGNr7=3`}iS)aBzam(sy6dN6fu5LNX%kxHcDAVV$L!>Y<a_mmn>I!#z;<OMBy$_p)( zhUhBA#iJeUv&7slh>Yw)ag`Sf+!`pAkNr!j%-TIYJ<U=ouPx)<u@h6nVQkoc5U*Um zfVK5?nEmrERO;~L^&zOVZmOm-11*|JMKYO0JRW1Qm>0CJxfRyxa{TcA2gp!YHwRur znt<EhX+u+YKa>(TXa$&K4WJ|~V0`QrDl00M&EcGh^iun0m<h-6`My2S2*8yU<J|FM zi|a`(6(6!P<D@APZ;k#AyUm8i#@#E6!L>D8aOm5wDTz$@bpPv^r{~rF%lQ7|4`HUe zPc%A<+v8&x8Tk!mZZ`sf19Tx+vofer>wHh*qYIbtLhE6Ok2o<;;eY9;i_j^JBH<89 zOPuVq2{bk31Tyz>+=HLv4M$qpSZSFp-0ECYmO$wqy82L25QFi%aTeT#u5c*CBHY3Y zf`2ZN>>3#O*~9Y!-NJcr7S6cM6oMP1;r~m30RSVpyW8O<LmU7A002ovPDHLkV1nt< B18M*O literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..93df1f2ac616c93ebc1489bf3422d9a08bbce8bd GIT binary patch literal 1263 zcmV<L1Q7d)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$kx4{BRCwCFmTycObr{E=wny)vfLDvO z9R*6pHcXT<<G@5CFm09)BQwSTn}(Nr;R|0GUm5kKni%6d<Euq8x2RdFi>|L^E-vGR zq0K@m7+EQ1w5-Ex>7Tpa-S2#!D@Cj=^9}rxZ?1Q}dp`Gle$Vguoh7oc-~oVXln4hu zB-CY&fNr;2>~OmeoR~?c#dJD7%`t<CPL~S|T|00naW+yLB4-!?x~_w92)Sh_;vv)k z#&iIS_28if1LAQ8W59I8TiL!3B!9d#B>k}*kfp!;S;wQWF7`lEQ<K!*-k#{|>x&K! z4kjK7Wd;zBV+2K~tN}Bhh>I&%znapD1&@vAB^@7hdb@6KSsc#<gD;A!t9OT@(df|c zD=YEw)2BoD67v3F23BN)jz8*2T8)W!E`4!<VM_X6p88zX83>);$dj+mh6}|4^z`<^ z*!UQnKQ{xdt*rysf4V*^Ns`bg9*KBX(<v2F9W8$M<rLWM(&KNud9kc%P%4$g?(vBm z^dF7QbQ+(<blG_b27};qik`Wj=O&E?@rYwOr9A5xkSchuR<3<Ll+WcnJ!2CgRn;JI z>z9!-0%F&`IdkjN_h*|mSw>eNmlY_N%b=j19v+5bsW=deE%|HXnL-Znu#hV0wbbPP z-f}Ep;hR(Ox$tQ5L1uuial9ZAxW(16X4slUd`>7U2qkH>&Mna0-2-dMwLpLW(f<?{ zElc8IbI{F-s$Hkj)|Q`+Ef%YMWaw1r-qM|s)+4^<lQS3Q==yp%+^cDts34FsL2gw| z1s67eKiAj&tU+ZG&vdHc+god_Dj3LfY~aSJ^A$*PX)#dNDsxhOZ4J*&*bcO;mVxJa zf{XQ_h^jc%89Bv#*W93BKp6w+J4Hk1^di-BszP)Mib4)6P)$|MBaP~PwS#yZtIlby zQ+z175+BWc^lq=W|3tJ@l7(h+YW(9s!*rGrq?#`2s6sB61&-sSz4z_TsR28MWK@<+ zEtP9&OX!*^SnYxoo4-9t*NV!j5c>E7cgrKrwBFe*?xq!Cs!C~AA(P2~qs5WX^*VEk zRN`4F$3&;~wpWk!^+&VWZO_r+XXjsf>zxl<J31rzLP=CL1`pDyUR99=f|!+SE*lj} z#Uju$@_N0|+IXhSHdt*AW*hvshR;q%EOsGv^M}x+ifKJI^4z?Fz^5k8hF^L6y^CDC z)2w)9$0>o6$u($h7UDi%SE63bil{kIdTFPrqvu~ccO_pair;?r$t;1H)h6Bi{_3dt zKdw<8I}y26<Ps$AuF?*Y!p0wJ6t5x%wwalkv9`80bIYk!o2x_a^!XN-qtPegk%ch> z*KAgj{9Bu8i(Pd2d4Y%fn`zkCNC9uR#|Hy}D+15U^<q_(&1Uz9R>3F7UieMQ+|MFV zEMWh|-s5+6`Cxl{Te8_~abH(g`1pzAzj8L4wjYp`%jGKjDV;oQ1gBq*;33lJ|Nj%i zywBWEEAs<~HQ4T#!y!UVr^X>WHNTN)L&@j!qQl`haA2WOFgGxo)63XJrcsCXh5xqz Z0|1Ok{T>Db<!=B0002ovPDHLkV1jNjYZ?Fm literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/matterial-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..326589e1fa3edbbd6d584749650490d33cbbe534 GIT binary patch literal 1004 zcmV<I0~7p-P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#j!8s8RCwC#mRoF0K^VusIlF76NkfxL zT~b|%H$p>b+>(mB2qF?yq(xj3R7$-G54=c;Xd^;Ih--wD7ioD=A^M24eQ>F*^@4}3 zZP?v2XJ(H7oX*OUp6WKi19S3a=A4-`zxl4`FxPeIPdAJHLi-Q>tu5#`F)=dXhx?kk z1Jy*ViZNE6^|%|-3b}Nu{#ffkpf}yo?_=f{y&34P+I<l$Yr?P;z;3LM1Oiq=etv$< z(j|-OZM2&@Iy#ASepe~ArSU|4{8!Kc&AVeyO+-rdOiD?BO&7vp7++~PUVrGzm#;Kt zbP>&&RYn^s*HLkC@hZ!*9`3B!J4!1vWS<Hlo1_%bPAza89uz{9q|@mWm>Q<%dwM>T zK2RtaqJ;~}Y23K6Q^Mil7tFksGqh^k_G&3aDL`A3Uy~t+J9~vA2r3_~-nIjWk%`Z} zz2xATBOI?Un>m9*q2P*Lwfh%gi;$njmcN#w3Y$HLuMR6$eZaU9@(wT;@&ro~3R(&c z<QnRD??oaJ3IqZhF%0_!sz1~b<q&8p3lM9&(YHgcUcPXC6XZBHj^Q2w)Ca`jh@&qo zEF=WnY-8e3e+=K0;SMK@Sx0W)xTbv=T2ryUf)NXtcU)r)59pX)A7FS`D@etV%vudq zgMvzEfs2`!JpeRt%?3jELJCj24AAS1nGGzElcvT;2bZl}UCu0a9e&Ga%!3I7c6IQ0 zX4OE^2Tmjs`n~rih^(9{5}>-m6j&CEDaSo+9*<nKd_^g8w%xYVCHi1Cpq`0&pOzm# zebPDIHl#d-IWJ+esfg5bsZ?^JQt}$^OOUq~WFCsfNNsX%^F9GQnM_hoyhk76rt!PV z3EKLg<s#DI1&TqVZQCdU>a6fg$PYfe-?BO{FV8EXwu#y_)<mOGLMd!MbLwQ90m}Zs z16Cv@)f#9s9_i7<Z5y>hyo1yke~kzuQnhJQsT6f}bx}HPcfNl8s@7;Jv;04JXxrmQ zQAF$vZAP2hPaH33c;)=XOX^T(DISkgEEc1eFJEb3Gk|OF-D&DIAg)8BKj<&NGUi~z zrp+wd3=>zPI?XLA8cmZXPu6oW$Wt8TmN99@k2WM_z;4c;wrsGGpvgbiNOl2`24w%N zWi~W8$d7%-T*{QT44BO3**<hk@8<mH-~-NV7%;)avNl&)GPK_npXLsfe}ir8pZVGT aCcpqSS4jPs029Fg0000<MNUMnLSTYhD8<kK literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..1253089272aa1a057b2301cd7ffc2af81e2444c5 GIT binary patch literal 901 zcmV;01A6?4P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#CrLy>RCwCFmS0FyQ5?s=_kW9=&K6lz zT(FYV<&21k9(s_VR*7bT&`UyAP|_+Q`v-+e5)?!%@U5pFh0HV@2~i+L2BDD;!79?C z+%jXj&Fyy1x!c;tY}?KC8+Z2~_xCx!-}#>3`Ef3n%MPHM94hi)9LGTr1c;&tS&}`H zEY*n=Xi{Xgv~(>j77KWuN50JKom9VjeccqwfR#2I3e09UqI}uh+#K8k11bxPMwo9P zHR@lIBrqsVX}*CmlNAL#{<BFWIK$8@EzPK0QGog#TXE)aBc8kavr{w4>OV!QX~5XK z5%l*xMDw8rHh^aXeHp(q483-{3n!iDU|m+i?rmjjFgfua&SOn@`nVV2aCqUN)Q+6J z0B7rEQUE~U#b`L-!0M7ExOln+vw;9YWQi;&bmG$oTy-8t+okic7Mif3yo{^@0d~6$ z-pQ{B%mxq)2Gd3?8iC!t(T={}9)tsaRNL2MqrC#6q`UI^eC!+wg_vkWL1bmn5LAbC z5ZD6<2x7~oa+Ft;K~ZF?NoeFpZD{BXJ4cAhB9Sl=n`SIhx*S?hoxt?eH*DWlh3d^! zRJ*B*0dGgfFzpXu<tjBn;Rc}hyYAptX9uiBA`Uj~M`4j!cSO~r#d+*{lt8KN+FP5n z%j5(def<(QJKM2NwZc)mQ+H1PV$hUD`|-0E#Y>l?@n{Qp@}3GSXXtgJO!H!3U;XYV zFdCqinq7_TAYlkZIKf~bUU?)@059;lL5E+xV34*pSHj$INX{#gMo?H}iEi9HIYc&) z9OqO+ultdH6^W%(OA?PBx&F*ivnTNlgQsf>_-yKHTsQ<6n<FlI&)#W&29PMAbn}@- zq{$#n<Ko%W5QTAc{<z#W^GxY=)Hu=(LeHock4jH5hkHHl%uqp?Kf|M6eqm;27XBGO zQDrFWNZ4{Ae&<aut&M+m!1Jtj%5wJUqwACQLd1D`DCt8pG*bdhi%g`BMsk4i?7R8e zSy7P{kMD<1rR`z5=8Q&}x=R?|4#&so$wMNQ>!yBAcMlFeQ6)(xUAP2+rG-Nt%tMa4 b-vSH(m>>V(2C$>l00000NkvXXu0mjfR85~b literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..45eabc3200003b2f8a89a11955d4f6475515c478 GIT binary patch literal 1077 zcmV-51j_q~P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#*GWV{RCwCFR$EUKK@>i_v$hLWyI`>z z<0UarB8n#7c+;4m5{#&cfY%pEeAW17VvK*lOEf{zcp(Ojuim1*Yodw83*aRYP-CeU zr3w~kcgAyOx22RVRFgW%$+WvW=bQ7LbG{j+Js6w=0F7t{<bMpH-P8!s@An%%zd!GY zk#N`uhr^8;7u+bCH4B89$U7+Eg!)2;CIo<KnjroIw;YY|5L5u?CV-;mA5?H4JWX&8 z+(dYl=e+pP!KU@??ZI_EpRcE+q@=I9ruM~-hFyJAf^q?br*Q(sNICz9!iaoZ6p2I@ zb#--CwY6UDY23U2;kKO(p~-<cu%@O1>x+wuWZ1bHM%MHBV{i0pI6OBVkL!Wbh43pH zHJ&_vTv1q9*uA(cFsR7<O<ddmy&p+MWK%}_cXs6Hu@|*<+waJ#EG~vETWetcyxIDd zmh<&PLqlwIbX3V)lf==8XETuiMNx95wYR%lzi{q!RmbaBYl~(ULRp|xK3}qQ34G{% z<c&suP9whsy4z%or5eL3T3JX!?jRRPq*OnD_Dof0$E&pp2z^;a04fkj(=<zN1_tCD z8y;pDgkY-5U9Pciq#uNb;$X^(nygPh_4eo&o6l8sVr1@Vn!ak~G6<BHfeSqnLlgmx z;PaPq#yl&d8dDO9graFIqiswp(uB!D%@XQ^ySL}GUcS_jh>sX+RxgLj6_wz$!#7=T z;CE~Urca;ojq+wOXv!Rp0osw$3xQ@j0A6WnE`N0YUfpb;SKqL)7Q9}sbfQvp_BV=6 z+q<{#<^PJx<<CW0(U97u55ltw$swn~XHSiX_wUw~E?%Hl*HovTtBg4^66)&*m%HHS zj$QjYD4)17*(2$Z3R@sh21$`f2FJr*yWFzLnCa0s*KQtrML%S}rGH7)Z{jPJL`I4Y z7LdSPl2c)(tBzdDt(+2iW}-r0Lgh4{I@t_KGDV8aB(X4Vok!P66oiUZi>*TRVdln5 zomIWI3{D&Z+HSR_b_i4S@$ULPDVekSkvn#UB@RTdnBriY3@~FumM{5GIQiw0UVB## zNRxvk=QKdsvywSs&)i|TO>bsdC+Xc&zdMbdWIi)<sn|JJ;PBDo*Hc<6oc&6Ag}tlH z(b9V3L~MA3p?JGuv0*KzLmGXlOrb&&$(&9I#gajhHK(eI`iWKOcDr)EKrxNUcA*M( z&MAU|;8}7ib0i81^f+p4&PNbqs*E&wemaC|DH6|qZIBMo{{B9c#ivvxI*|qk2Mv$M vllO}m)`vU+sd{e?{P@v`_TO&!e*_o+dEc{a|06>X00000NkvXXu0mjfHZT67 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/mesh-road-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..1cb88dc8e613f3321107c49023756d83c3185799 GIT binary patch literal 619 zcmV-x0+juUP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!4M{{nRCwC#mP;=~K@@<`v~`P7pS zMdP&>k$BV=YjzU$wj#0f11#*kV&O+f*!T&A2ucK{5fq!!Yn*9kxLUV*?^PC@<mAlj z&Nt`G%$Z9VWAM{rgI|z;Xhy}M*U?ztp!<=#qGA-y2<kR!0Ch7I3<mVZAD$ei2TfBt zgbw3Eb_gL0R~HvY83`|XwT5O6At7F`s;+^YT-uJs*|N&YCbT3Ydy3!<H1oV%P*?<g z{sAa0E2ntxr?4q2)!e4Si=m`EqzD+wrFr1-dSmU)J|BcGgQZw>ilNV_%o0?>P>1Yx z2d(#b!G|D+BR)+y9M;bJA~b3Ws)Z>|Cv9x<#k^A#1#n+MrqcbrUHJ*I6kY^+&`jHG zc6Uo#8?>S(3xUIf19*x=c;)m%hfyd3JJC!Q6_?Pi?rw0o@?&0c@7}j6UOo=QuPH8W zT{8tU1Vx~m1FLK6XisnN2SBm|4DN1kc;Wi;{A^1MdNC8;i9A<6?f3hUK?D-_kI$Fz z9IC1+_f`>29H{DnQ$af)AMRmwdCABDzfFKL!+W~8ydr|f-o1PaG<oaeGCnp2Nio-f zewmT9Knap{2_(sfl$lP0i6CuzqQ|i^A=aKYosgvzd|_rRK{25Zio~5QY`P-TA2XYg z<b8WK2#uP(ry!1f<g54blZC2QJ&k_%KN9&fKijVY3;+TYrPI?%mDT_N002ovPDHLk FV1jOv5V!yU literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..72457d069119a7920ed5184200b0c4b168a8b116 GIT binary patch literal 936 zcmV;Z16TZsP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#N=ZaPRCwCFmTgE=VHC&z+wQ%ynwnJ0 zvf`FaN*bkEE$vMUD=KB+ho~qb3aRkrL-eUnR#a%I(ZJp?(>Y9=F-ys%mM=B4$r3BE z+R#!{+oplum5uK1ObCmn<9gumJoi4&y}xtL^Ky<rrBbZ|pk<W?4<Zl<AQFi{5CkZa zvXB(1BwWC0Vu}t9UW$N#00@Ob@FiC3SGwP7)4HG>aD`kBUq3(2BEDo~WCZ%kN}6-a z<ssidm$?2<k|a2&E2Mk_e+wlRLx?FBl67vv(6ss#eCThT{BCBWIw7d((+f2By~TB7 zhF9KB2wL(cA4{ltXtQ@h{Zhq0)M&1QvG*PJg&sx1qIjs=?&Gu7%rErXiz|>zmLi;v zLd1d%2qgS)_1Q)KpbuYZ&^c&@Z00hqpY$*l9RoHPzqFyK;U+hytV!efdG%zHrnwLj zCkb^+Dfhf>Q6v`6S`6L0GH6Ui=(hJj_d$=@l0YbyZSfvj`aB<<tXH}FZ^2|~gt_k{ zQudtT#$s10aU%aPv~M57j}#-jDg&~4p_t__^)9@HhC;*yDL@Cx5U<!j2IQ_!-`Q9& zeIe!&zBsq*JO+k_P*j`i-I^BH+(C!kj=hRQa5|mi#w!f>QEN6KDLNJM;5FD45{ta1 z2QalaP99oNug2#2t1-tv2oA^hKgRm3JxDLl!a8Xv)`q&*@p#;61kaE{^(bp{P|f38 zoR@K72^eM=Fsu#_Gh^d9x-5)*cjDyEQ()NlKiIV8l4DyF(e&v(Zj@$D9GX|7Mx;~@ ze~Hu$90fCs12v81xM$Sk$l8POp(J1&4mcP$cvJXx#4nFVUfn(P_IG=}d++LU&|$M9 znx<iFtp`E6^B~14NfF4@Wn;N-5W+(?az?nzlF^TSe2^HIj0)3JTq;OIda}whXs%w3 zpKR!E8%q8eqL_$iG_};S2C;^)M!6HTyCnz_Vr1)4(p-%l^<~%=wfq0jCs|Mb3Nkc5 z!-2~ux&F<DVvla0vjrE%Lq#Ib6V1A?O=!RS+UrCvBt%nFru+eA>(Hr(iYc+hX18(W zA>Y#7)#Yj|1W8fKfx&Ou>gp<*Bq{D#h}hl79s)kN7FLzN1Q-CAGa~r;0-b{Z0000< KMNUMnLSTXxptk@3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff1444ff9cf88ee64e447e2cc092842138986b GIT binary patch literal 1102 zcmV-U1hM;xP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#@JU2LRCwCFmS0R$R~*N`t-YnDIBx~g z!em1*sfgOL5L&mHJF^TnlSyWlNi>Uz$#mJ?mn_SY?P*VYSh6MaW!Z}nP22>=X85DR z1gX%J7NxUj>VK^wtpnQI*>lg%xfiIgmco*9l5cYFx#ynG`TfrC_d7>7;`7-6G&70F ze~U4-7&y>swTi`7Yu<?yk%$<HM4Al>RYXfs5hzLJ9m+Wm9zGBalmdhh&{goQ!o+zD zN}(tO(CPCJr3yGtPZb3U;b0}Fe(L$*Q1G_jww$pU#W<X2&^idy(yR1`hby8_qZPeZ zdYY<i)yMv~wG5<ZY_<ZDQ{HipWHDRt_tccFZSd3^%lRjhI8V>XB!^qP<AW8GvB`?v z&PLSR-3Z;jCf#}z7FX)tISzwTkW@;I;B2}W=xr*sl%ZzR4%o}LqOhP4XFcsrOZ}5k zjKg^>kf^$XYy-Q7es{*>3Gv0^a$Feh!jGrF$CKzJxaGeM{Nm<gI)^%(%Yrfv=dt2I z)QHNKr&n$Sq&2!V=<{As-?vq5M`>{x21f@F439~%si*7jJ-*{!w|0H3&hEOIJ8cNd zQ$|h^=4ai12Xqbn;hc%b#m^3ZflxSz`;YJA!@Y;qZ*IpfeEah^rknS|?gC>0F8X?# zHkEEZW+}AD*?~|k)f0tVn%NzrA-_~pxeevEm+|J#x95RsNqb=fHmoaxneFdK`#wf0 zMa0g54);>-RXI#rAZ3WOv;FO=l?^-hsPBtg-{o@%-U#CTh7Vw`tU-N^3!z&fDKI`_ zTRM7qAW=olA2QoHC;zp8WPfQ3v#1!!<Ub35Pofhz(S972WmRxg*QtF6_8vkJ8+*>v z-MDzJIjMO{6)k!>T_MW6I(pAI<B7Pq>y_O|$TDQ6L?XRaCmAsHj9~wsgOFMOvy^s1 zz4z8W@TzsXv&H@VeDhMMwx{Vbm{#DgfK*vpg+jBa0cXLooW!N!KhZzbkA_#@fl*is zIhjOK*1)dX*WlXj!r1koG!c7bT0E~NUVFKFX*yxrdwykk85c`LD@MnB5Cj2&QGk(& zKc8$njuNp1TQ@t@MewGZi+qy_``<kPkI!Q|)p4TnNb{$ynNFJ7qb(gMqs&UeTz7u3 zJ8Q1(sINufs#jfvK`=lt80Mc1?Afu!iA&xAXW!-X{@3fI1-Z+4y0*5~uQqNh&v`nG zT=m*oyU!e5$%$eVjIqx@`SQEx`|jSoqZJ35>s({aQC<6x>FbpqWsB#YErWEyp637d zz@VT?#^ldiqw39pt}2xC_`NqaJv}X&&E~uVXJ%&93pc+bwZx*)W~NoU;r}bZ0CSJc UwaM;z^#A|>07*qoM6N<$f?l#JR{#J2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/missionarea-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6a8533f5aff2af58200679d7d0e379261509d216 GIT binary patch literal 751 zcmV<L0ucR)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!kx4{BRCwBA{Qv(y1HAzg!vKI$O9zjo zlcr2x1H{^Nv$TKW6sx~~|AO!Yx>*XetVlpm07Pr|^i3?Lxuxx$JwPA+v9h<dVK6o} z0$Tj17$_!7Q%ixq>lYIjXHZg9U{Fz3V<D0*lei(#EFN@V|fl$Vy2{GMqSdhGE{k z1q|=sy<-51SpnrzL2Bw7o5M-5l#x&|SX<wClAVoR_UEskV7``?HiLwOIK!z^ry0I{ z`TPl}e<dR$V=9miX=!RWMzN)p)wLiW^cUpiGTga$pW*rQ7Yrdm{$RN;U%r5{5X0xs zpBUKL*%^NR{Q3C*zyGcs?Jb`u3f|wpeix~$YcPn5i!oSRn!_yw0UjP61|cCq1}-ix zhWNNx1|}wE**|~&q*4^8g+-;|z{Dc!?Bu}k@83TR`ID#5GTgjzlOe$0pFvSUp25V# z7?ik*3kplL$+i^e>LL|YRR$hzZeY;;LXrFU=@Y~1RjV1~6%-gWG}ORif&RV>Lc&5I ziyO(ZG&3tV9B8Smg@rl8_a8qPegN_J??2%3Yu9ZAIf@}D(4XN4Q2aNL4FZ0?z94a{ zjLe)=k}du7=Xa5Uq9Owa2Rp<<sKq~i{A4(L{vyNiV@DaBoSlI2$i(pD=TC;8KOh$C z>*+8Uo0xzLkK~keK3tZvpbI1>rGQebtgx^!!_Aww7+6_ZftZbfjg1w|Ub|*3gP52& zgR+tm*b(3?zzEgM!NCw56u@xi^l837e|~_1xsa$V6C0aw5@-tEBF@g<jzLgR2<QkF zutQi_SfGxBu+N@7%W&fO35Ngw|Aj1Byzm&wg#!#kL`G`^3xVAvmUxesFI!?xwxy%Z hl~GFvouvQ)1_0jI7tQBjYVZI6002ovPDHLkV1j2MTs;5) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_d.png new file mode 100644 index 0000000000000000000000000000000000000000..144099429a40ecefc55e2933b781b5ed53e665ae GIT binary patch literal 808 zcmV+@1K0eCP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!%1J~)RCwCFmS0FyQ5?s=yZ`2r7HgG; zAybzIskFkt7+6nH1Vf9ag>L$gh)AF#SUIBn2POnc*9iQx9-^0!KoHUrA$o9GAWeeE z)`Oer+@_l@#fQ$h(1TcWQ?EZb9Jt(jKj-^9-`_bug(!-r01S|!gAAciD8RBTIF5tB z3m#tJi#ZBZDFt0tmL3|71`NYMt_TDcbpAkafI=l;ZeAYttJTtq<cjt6b&L!T>m*rJ zq1-?k^#451LxR$j$_@M*f>H?vdmKCwCmZz3<RosI&2ZT58{JH@Dhsr|wFQ+`l}JwB zho-i+&3n7s>Ku*t@NN1B&Ydm5v);ac8)~<JFP=N`xU&<{->aytu0~B=-Tz&@Q<!gN z2DbWotVCC!N>ZW9VvPxus%59@%%x@+jYa~EqKp`4E8w=3Qg!zojhOfQamQvuPfrgl z<>k0hVTtSegq6M@8-vwqfnKjiLxT<N9UamfFEAmc)R|hX7ER4f#Gwx9*<Iq)*5(!% zi;5CE4&!<07O6R!%7s7>XAA~}LP1I4c7Sm~De%L%3&%4vaOLtP3=R!pc{u{{iHO|% z{B0R9=AL$UbxFWNQs`p$Q-s42lwCJVMZXm=yc&Z0mn$))bPs!-{YX2S3X{o%(YGTQ z|L8)0zcVK6jr$co&AH%O{0y}!34Iq|#}B2)87+3-(mWjE126&ubxtxK4L(F>Y6c9M z1*8p8(0dX7$Sl6jyD<@%Kt!<y!=V$XJz0Z-^z-p6rD^>h`iUbs8rV!VIF^zQhGj4t z^1?GWjVbRGUb+Xt@Z=sANDoBj(<#}wntct0Sr?FYFe7mzm6VoX?LY*#E9|mddko8N z_EO5>G}(X>lw<d%P1yh?edyjpm4ea}Bqt1Q59JP@*Gnf4#|uL7;!=2Ebo8B$=LOP* m^I$oab~AEtJ!BOB5nuos0N}c(-tr3o0000<MNUMnLSTY>`DrTv literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_h.png new file mode 100644 index 0000000000000000000000000000000000000000..9ca94ff584fb3a1e43e0b10f6c3875793efec044 GIT binary patch literal 1010 zcmV<O0}cF%P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#lu1NERCwCFmS0FyQ5?sAyWYjh<*shJ zNtO#E_n<lL!2+Xyno7lmreVF6J%we|OMfu4my!?~{X<D%RuI(7qM*o!6f8~6fiHV0 zMp`;DcQ0;nqn&d)=WhPVwA9*uaNaKGbHBge_xJl9jc0Js0svL?2oU>_=y}L9pxJB| zOlEV;iQ|61;P?BhcucU6Y%~Hv;g~}iXLM{-;0Xah2my&hZZSQKN1+5TMgSTv_E3U> z@pytUU_|j8{x285>3%J=w6v6(Oh*5;rlwcFKrsQv<1vF$rT9NE8|p7@@pwGj)6>(v zR~j2%g~Px5gz<RF2K0}_zTRHp&h0y5ATVLg$XM&Sar>5gp*tBbqLdm*vrJ%fbF;1E zWrv7RSXWfE&3*dJX%C_I%@2$!WqC$44I0hTu*1W{x~o^NimO(wwCVMUkei$9u02)j zK??v?sA^i(NRjj|1=-ftW_#V;Ego=G=w7^d4mmkF?wS)P{s^2>%6N1*Ak|Te>C=&s z5uMZNEY<S7{bcPaXm`1kO}U%gM~)ux;zjy`@l>T@gn|2uR9ZL7<42FgJ!NH<!ooth ze7QlXtf+JqmlTgJ9vD@MFjI<Pp~TP@5Yp1p6b7uTuV>3a1v(rRuARGfj{PmAbA$0B zMq)LcN9l$07sM4>tvwJ3Kz>0!$g&Jh=XJ$#z(IjK#@K{eO0W?aF%pTi%v|clx;pXm zr%!gP)e5_dOW@hlr_%cmAACovt2^`Z@+5?o(c6^Dcxp`<9cEdnwe_x@0SgK?!`X9Z zq4jPn*z@h3)z!y5YHj`iRM0ows61j>pR&<6FBg-UF*p@NLqkG;Utg)&Vu7;qa_H*n zf`Ng4<#<hvclLL4<DIY9J|6OC2Y*c3crCAF3R#Wwnn@iFEWKn#<%9e8MZI3H3&<02 zv&9LVHUW|{^zi)c6EVe*;?G>O-p6qqT}m1VDwD!Qa6EJJyKDmvbOwErFDo_My&=`+ zoqgxrO%pLv)PeV0?REt#R)aZ7+L~9`X)>lt3?PRjK@JAYU*)fv!(TpbPoO_S)j$HD z0K#g6Z=K27nPtiL8ue+?v`ZIob<{`%ruwXWf4Nti%;^s9JKVFZeYuE!o!+M;nu=sY zic-1vP!A!odq+g}=`5x32t56Ah$B>0O5&_kttoo|2ZKQ&F)=abz)&cpKDb#;A4-x` gMNiyb_&)_00L+;Yxf1in0ssI207*qoM6N<$f*L8}jsO4v literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/paint-terrain_n.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d156044d8559f1b0bc7801d5564717858346d0 GIT binary patch literal 807 zcmV+?1K9kDP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!$w@>(RCwC#mQ6@hQ5c5L{h4WOx(y_R zw#k8-U4~BBvLTpS4TLL;e%39*8j+SwiD(3Aw&({JO0{Ys6;aTpFeA%~HmRegAQXw1 z&N!jopVNEf8#!HOoRqO3I`DGNJu~-v?)RSWoO>0{Is4O1Wq+alhi+)oiy3TJMUVw| zhjw5XhmzwX$=mYpqZ8xB&lJF8=<Y#j06QtZkW%zaqtU}6y9-bldIVq-#TGyZvLIeG zD1Z?H8j7M^`kWkog0YW)HbHq&G6l>Ku$>jb$n5tSmP(}r@G0gt5zr#pZm7E6?0i1o z!{aAUMHUwp1h5IfXHtd&w&b>TU71pLaby>(J6gxOySmd;Q{S2ad`%e&fC(+;vUSZ+ z07K{!M`~+B`)g|0#Y7@)nr8jj@X!S2y<}rS;BP^xGSdRs(%LG(%RtMr&W;WZPErh; z>|81YEC>Zq6iQ4K%AU%-Y<6yrb+ot71K2P!_-T^X&y;|?5KuHF->Xdk(Zv8|Rn=AO zK~K+DTz}9s?Jj`RwBc8PIBgtpBCXa|J&^#631D@2A8To8W|_<nrmE^46igcdnY4b( z!0f$e8XArI#`EDd*-L8}FDol&(L+(z`?8M>yy>4u(ez_pqitZ4tSs>Im8-GS_2&-2 zxd<d`;2?$M1?>g&Rv-`vFJS+bl$0=4)0kmw&G@(YKkB+Z273n}6LO$(%vGb0Xqs-I ze~0VGwr%&`xYm}TkQNo!ioLqq*3Eu@Soe6oY!3vwJYMg#j%Wy0rZ6SQ9bV}$=Y*=L zjMKv4q2HtH<lK32v*Y>{xuHti$X?anZKb8pv3ADWSAF+svwK|@IntBM7AYk8|8~9; z=@)yc`ozs1f-15-QgHV;3v#4G-f?fCoc2o1p~}pz3Cod{4CNA(ujg>!u2)jgzftpL lx*@QFC(=LnHTYA20RZ(lJJ1xboL>L{002ovPDHLkV1o1veO~|o literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..9867911ed724af340a07e3435204d20b002bee47 GIT binary patch literal 1070 zcmV+}1kwA6P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#&`Cr=RCwCFmdkG&RTRd5^K_j!v7L~L zB+!^NCP*ZrfP?~60%cQG2$ghMREmTI8w3&(JJ>|x4`_w50kJ?x6tM`4Zcq>vsOkd< zC2c5q6yiLH-PDiR@gtt`%y6%vJX&aC6Zc3*9(%^~-SeIAJoK@#v3<a}@CXesMN!Z+ z4PDnU4Krq##)vKf{XR3YcW)oza2QopX_TeYsYo)N8JD0{z=6R*T3TDHCu)>cDiyw% zn21!vk`Xo<P?~(dVHi|FC8do9ykVx#hsqr`Lj-rnK^4Jj&7hed&$IH`dt_IBsS2a6 z+}8Ssz`{ASlkbyAETMJZPxsT$va2Oj(>v5!C2yTMg=dwy+boK(V=SKfh+KRb*L8Om z)Gk|;H#bo(eU7sdqv`nrc$Y3<?&(4E@~ljJO8$Br+qQOw-qMMeagzxu(^snBn_f7I zesCE5+plRoF^bn@P}T!n%Ou!+<PDlaZK4*<{}-t_@)}-o19N|rmXjZ&KXVAY98GV( zNbudq(c8-SA{~@kI*6Z}z!I!%MfH}TcKdy7zH%IUset{#s7UNjC{7H;O^|!@EE&6# zx&Ffh1|P*<i<6qUD01)I7I;fgO*2F^U8n;?cy3AnPD5f6&tAYQDa7|5#V%UpX66V+ zAD}QZiB-(ud0zcFHGC$1FMzX=!p&ZzF!~9Vs|x0jhuQ}1S{{ZD;KmmyE-hl38p(@i z=zQW}b*%l{)Y_JMRYk>1uThyZSnnF<*Tcss<)XN0h32(+RJTl9G)lO?AA3Dbac+{* z`gNTDpm^JLnh*D|JamXvf^3#N;txMZ^2n=XZH3JDXDKYq;tvIA>FT7CO_RQS9!KQX zah&=(lA@_Jzw{2}*es@HVP%pyxfIz{O4Lv{hLxu%?iFW$Ak=dY{&0}gg|Fy%<SEoZ z@D`f41uDm$Z<n9D`W~b#K=EYbR#3#fqNw4*@;sH*dE7#V^p$B;@w}52*K<?se&|Ud zMzy})D+Q$)VhD?Q=sLgWN-EUsj?mh*M_doHvHUanneWI>eM324AR|882Zsd3T6IsV ztKmzEq`T^00+GP3aL@G!NBRl&L}~6FU~&9Il9#9G6$f-Rw_3M1C{}L{y%uokG6Ff6 zbzKwXVpP|W_WphJ9eW)!6cU;6go>SiC|NwaGi%D9QT)L~+r7O!_SSLHo$Om0g)a8c z9p4WAu2iYqn_HslGkvkldZz9z;Px$4P%p$MY1hO-;;XCj=a`7p9LeT3#!sLAG9pKk oDRQFeQfW#fR-~Blk>3Ou050T+tehKR;{X5v07*qoM6N<$g4Or`bN~PV literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..e7fd0b0fc28a7b673c86aaddce899116e69417a2 GIT binary patch literal 1237 zcmV;`1S<Q9P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$cS%G+RCwCFmRoNdMHI(pXUAFJy_<Es zZR}jzC?XAN(jp|<hqf<(5UTnZ^&{}c$KeyuCqUu_2=GvXAX25$q!ltv;y893ubtT0 z?A3djVP+jyp^Za`a7Q|_yxKj#ng99EnL&R%eR>H1utY-ygCB=99w7!)6h+J`%D{>H zsw%3gx`Z&nq9n_J1pdIGjI&qY6A>W*1VI4EThJEMU_1&X0OJ4v4h|klFkn1HFa|iF zc!&oN#RM=O!VF4RiqFBUt*wr<D_gnT{QD)jFlj-azW|s*DVwe|IM)ZHu`vy{*Y9n$ z_gAW!5q0*?$6L`vD)=u@#)FI;gsVL}Jl_X;Wn+P=A`Hj5RNx#c>)(BK?O=B&%8ZE5 zk)y|fOy__mWFMXuj^l8aWpUct<0;><F6G|40o+#e%8;D3A&<r4$`9|f>brvLx_xuJ z3d&(TY9tcQfIF>*-+u8Ra4gZ)wF&lr?Djp7nx6yh&7zE7tbQ8fc`t&Yz5VkyQsM4{ zm9(67c?i#tmIh|>k|1={X>y2*cOPTli>4>X!2D;QgUrHBI$H-QAKd}cht~lRxQLL7 zANlbNxAo+gNy{|(Am}e|$e}#Q$bP>U#rz?<`N@~Y-hQ*P{53H3U0~Zy;JG?D{Amr? z4s12jS4-m5j1E1wQ2TQ&+i^RvpGO#n@lb%-N~zwb$iZ<OLFueTq})r-vqo&Q20Tpz zeuREQ_qK6)?l0eVhuY<eD349(tx9>)o|<WaWRmcZKefp*9>QnT6hcU1v6%0=PO9S` zfyVN003W#kDRICJ@jw(vBs#mGy8HF0(P+eD@u**W{v<zs<Ms}S0757IMok$H5`x2h z8XQ~t^`P$n+_BPFbG;7CthYy|e)SEwM_gJPvzY{eONPe^+HfI%<mmd4-K>u5?E}H{ zeC~~0tj81FbuXiXJ}sa1%MKrJ93WxaF?<w>c|;SZih@iw-Igl(#TQbb<cy3gnn@{R zHh0?M{>$g1!s4y%2%+AlYW-uitfsv-*w^NCBtN+Sy<Of+bDd7aGma95VaIj4l@K3o z4S6=N*Q;@@x|vAH!@3ZU+U@O)X=&!&N<5h&2qD6~-Uiu9FzJ7=vAUp01ZO9%YP3<X zLtmpkm#~i=?I3D3YLacVa=^7yPPr`dJdXp{Nw!MGESkNpvJ~_gIjP+7(ROyXXAptP zgb?r1QgB@tVou0iHd9%}qR(o&T20vXa^9?0C#drTty0QM({oKGFWUttl<UeHoU`H6 zWfOYrh6xblSptqG4S&VaaXqEvi<(?g>(5pfHFdAx`~EYYhoOmkPVSyE$7kF{22B%g z3j#O-dB_rT()g7YiY0z8tt{W7i{U)aV~;s>EOFOJOkVQ;TvmEgW|UFm+Wm(=(rp82 zTja<I2Kz;T;D4>@1qNY;ViR};?J)@McBBD7!}PS^gq}IfMWn|Fk2ZDzHVi{dBoYI; z7eVG+LPGbMrWszinR_1{w%bcI-r5cSZvh4XnT3emZuGnr00000NkvXXu0mjfKjKw0 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/particleeditor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..d8c6266af9d660cded3da63de2c45e61774f9a20 GIT binary patch literal 914 zcmV;D18w|?P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#G)Y83RCwC#Rn1QmK@^|eZP}LHwia62 zLQ7jpC?7>)geWA6F-8+LMm=~I{}lCt7th{|7w;Uv0}>9NOiYY`XerXtmX>X6TiWdh z+uikzE=k3tv@2+0e92_y?as{aef{1LvMi%NEhPF2@*niy1JyM8+3)fGq>r7Q7^WyP zkjZ40;@go-O%Pft{ZM%8Xwbvb<8oes=^@bUiC(XNUoMwlM>a#%6AQ0Kt0iE%55r`4 zblZ(4(-_0D_k8|AGkhbC6%@co7}V7pH!p*F#_u0Q1W8<DSvF%fn|o=U?yk%0HR658 zlGE$A?>vXe17jNNlao`(>-C^;cxwREXF9#U#2O5lTsG_Fi^bW!T#ko4OTXo*BubBA zGTUr6^k8NNjf@N-zR06MU=-Qy4!otEqNp7JrYzR3X<3rA&4jw4r;D%V!@dFkEm+Ra zFDwiP0s&Mg6j45(N6BOY3H!n(fG)Y+?j0n{Qv^XoKd-H>!F~~jtTw08myKWwgYtX{ zMWay^i^b3$fMU^VHFH8uV0L$RJ-u#s+T77`$=>7C9p!C9PjP;rKrkg7kTVnv1`&Rh zNaBaB?F`EVM*CJcjxzywtrm;5QYaKAlJVFZ#Zy@=W$;0lBuk}ArSdYL%gw_29sW_R zRP69g>htw&JDsj*xm;p(I^6}guiwz<?xvapT)vu4rI$rXq#1^hAp&!4ZS*2|<=F@Z zy}4XA(b;L?fVW&Wn{@++$KlwrV(>53Of-7LaT`>cg4W#8=>&f28P;H<Y1#@NdD<Dq z1f5B%(P)Hfb)N$6##1S-i|2WoOC}z1iA1TssG4qrxW58$E&DYS6Gf4Ri(=S%f+SJ5 z%XP|VH2DB{1oWOlVb4pFqySay03!!r?~3B7+;Vso!V8Mk-*W|=B;t_>7e^=!>f8Ro zAv?pc<1&&jf^jTR=^T9jsGN~n2CXZo!xs<-BlKw<M_^}Y^fdNv27u4Q{6aC3sP|UQ zdn(rrNijrJA|b4=d<Y4G@D$c51&lh3b7Uc@kOS{=6t#+RnCqgd_q4@G)RdW2#Z#4X oP_=}r>OBUpn{N&$CEo-X02)wUvprYo$N&HU07*qoM6N<$g8p5m@&Et; literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_d.png new file mode 100644 index 0000000000000000000000000000000000000000..08b3b864156a2316d6c2614ae4b98f54b0b834e8 GIT binary patch literal 1503 zcmV<51t9u~P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%fk{L`RCwC7R$Xk9WfXq;^L<~xwqMtF zTgx`Yh5-Tjn*uSD1c))3B}g2KL}Jtn3C2WYVle2P7rN+;F(K2$NTL@)h_VQoCXT=j z3XCWVk|7MnfVnZ)y0+{8ujibuFj(7pvp0FOu6@t@ob#M>p7WYIJ3CtdbkPoA9+t^u zg2iHi)oO*Rs6&dXv|AZa*VI6B^AdPG9+=H$%-0GACj#Ta$u0&Jz*VbP!*IF8qxo9d zY!=6RdIAy_M>yYry!d`aQ6MNx>0E$gBNO=b$nQA!M?Z!J$B;^8<a<q15LoQThNm7w z+xCs{H`Z3YN7Xd^uh8~)971>3891Ca_<SC$T(uZBU4w~!OQzC@MdRo`avF!eKY^_; zJc+mW?W|ZE3I#VO=-%C5;Bx;kmakZZdcPM&jROvc4Hm^Bm6^-tkVvGlC=kP)u^@iy z{*#y(MaS3g!a~3ggPWMuxc~<TZez{br9zw6R|A(zhs{nD5hz_Dps8dU(WwMnZYK=G zfr}TeV&9%G@oDEfltb$*U~4r>FI~KamdBT2VZaApgBR{v15QJSW}{-TPDh%{BTb;O zSPFJ>MpYTagtKS*@zv*t@z&lQ^qukmu(?vv9Q)}snwK<5gbY~cccZr61#g{!f&Lrt zkOOY7Q~rAD_<I*>eQwk@c(9<U4l7nXieG;2#mMj&GMOy$`FwRyu2NlB;cqBX$`RIh z9B@%S4VN894xYq!ok!s?Y_g3Sov5|T8Fs=;&NVKqhtsK}=jd6a(`n@90_C>w`#n-L zoI|FRE7$JOsA^R?^LO7MIzBu=`AiYr8g#oVXLviwF~jXZ1F>|z_cBGAL3T#8WkCo2 z89|NPkSgV(;T-Z-sVA+JpVGQHbO#^5{|yE%-H^QT@0v{&$8@r-t8?Spl`&*<Im&$& zg+ie+XgZyhKIFFGD&?YK(KcIHyiE_B4Toab|Jh-jI?)G<)huV(Mxkx9+mK1;P@o8l z!1>D6n<tOLlTjq8|I#U@rBL)z94*NIT%mH7K8Hhbe7pacv`x$UWdxK(ifkjHsNBV7 zc>*laTNcztG=(N(n2N+nZ4!vbl9DTpXtU%j7`_#pWwZI|m1y0(1`~I}h)0uR%PeS` zRU9rtSLxHG%Nwa}A_A6-rzGN3GDH2C5&C8>U03k(t6Q*b=N3f5aXOnpf^6cnL&R3| zGXJdm%jT5DJ4Dn>(Ivd2k%UmkP9$iTR%4R~AARzg0LDoi_)K&vE>T8^^6~L7S~e^d z5i!A3JyM<)KO8uY(cy6t00TNvtB}%=Ey-RnFY7im<IVTlsmh%aAp?ejQ3UTsFhP6x z<`~hM!=~q+5ID2tz7CfKWfj`?QY(HucuFo6X`2@JBvJhQ_ExmDZ=|Y(<d7-l2pRD9 zND%+tyo>83`deRIFKQ(sZ{ZFwSJz%1PdqKopSw;*(}0v)7g4uzZ8NbjA?IYuQ2v-w zj*tN_^j$^Ex<%Nvr%k9W0$SyBRZyma#}iMBzj_BLw=pz^eejUp>JClBfbYm=a#A#$ zL#C7?WWWzPUcdJ!lh?wGX=5#gTyF6^<nhGQqPy!XMn@;$t#!feF=(RLc(PIS#VX~Z z;T$rhr3e{BluA|QuKUjxOXZQuMm!IBJZ*brlZZEK>9s4jXmDrlwG6k#re|BFXe5Wz z5i+2rYO`jlRhglsm4Ml}>5&E4wYv>F-grhNjirs-y`-nbw!pojVM;l|vO}Ca8JY}D z2Kaec>2X$yn9H8euy`{tOIt~47*c+pE*j3^!_Sq`krDngM3JiP;mB0i$&)7nilWjh zTp{5uK1Iqck+ecVbhI#IMwZ?`3?fmR%GwXnZvRh!0RaEMRqE<kL$Lq=002ovPDHLk FV1m(T(PjVu literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2059a23fbd48353eaba218e6fde938ac2de7c290 GIT binary patch literal 1495 zcmV;|1t|K7P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%c}YY;RCwCFR$XWuRTMruJG(Qxv$H$d zO|zj%Fl~~}no`}0wZ%$O6%3L<i&O|zU-YSh556f1{-B^wQWW!06~Pu##7ea){vf0; zjfi9`X}jBQ($pqRrb(LZ{?7i)jOX0h-E5jPeKFo}!ZLI3_nq^dpF6hb_;@n_452Bo z`74Pw!V_UI7*raA!A)meR8>V))gj(64W%Iv08=k-Iw|qY&Ce;kX#(iF4(0}%XJ%CK zmosO>lUJ^Fr&F1B!!Sg8&T*V3dp*+~UH8SG8tjd=wg$D0NDVaNLsAnALkF;N>uwzW z==k2)#YCShd&EGaA6i>mKokYA*#NXcQOsp?;mOO>;Sb-Q=nwaFjvjgS`LVl_ng$Rb zZyE*|I?{8-noaM$@$rFdCKm~HcYq%WJ+g#^PO$TKu;HJMXA4>pTC!VVabZcEp1Rim z?(0W`Z@&M^>2*mB6NryDh!i6=?%@33t>FU&tq=*{AA-iF0C*J-xZNTM7^U5gl&Fke z)}dG`K|ZIULvD~g9*A9vN6=Y#?VTfcAx2W<W**nCFFyTxFXk@Nvm*@6Elr>_ctQ3_ zNa_NIgJ-0quZf-)izU#=8NmrWDu4pO-_eQ4u}@B_FTVWj8mWoTiWCdUR(Yt--xA}P z!>&*Vi;xJF2Dv8f<=9o&xhre|V17!aGB}+MMrcDvNWzZJZE#^C);B-95Dhl_wG~1u zQjRw1<ua>avfqC`7nUTqSe29_Bto|&GW+$XQ8;tzHzu{qC4l4>!0mB?gy!)|;PrW+ zsd)=f;Sp99ml^U9A4d|Ayww+1C$Dt}0#?xwFiA=gk_l+EF2^R}^Wl?FLTMdN0Yt&U za_z$RE_6)txM53EBg0xniaf-}8K%xE%BWa!In^$E<qC<wa_DfPIZ#3eU(3zh>^uw~ zJq|Pf+`#t^%P_Bk)4`nc2O1cbQHRZVkdH-L)>mHO5~&ZVEvR6~IJ+H<!{4iWIg`%7 zi7&r}^M74p@9Z4yv9tUNg21rqNr{I=sv8vPiUjl!D;J7|5|pqdXo6J!)k?m;{IlsC zd^7SRjQ@E7w;?c|Dd1Uhh^0=i$0mt&s$MInwo3WhIGdJAWkehbpke>z^92yFU>qeK zqc>pT=F*zpduaCqu;alHq?XgTAJ1g-kWObA7EPgA^_tdz;K@5`nJOz*-*i5o4{JDS z$YUxNW|g+6U(}oZ1CK-R6OTYHt6|TUnL?UYfYj|2!_xJ-8Gx+B$CCuaUX`z{y(J!- zNQATKX%0QkVYF0Gyxm^wWm=6}{4n&wL1^FJ0^}fz5LKZw8I*i^ISHYj?Quh2J$Q(( zHU_I}&%T|p3+LngH`N8v>ys@)o@YW>&H;pby5P{^0qk2Bi;xJ@sT|x+rkTy(P;qFK zG{UOKu8l#`+%Q)zHpMS%cvX$!HP$c7GPVg;E@xvYAnEo$xep%geVAPmltYq~A|$d~ zOG!}Y7GZW~76^l|>anXgja5kcYHtP)^^JXX?7QI9<W$7Q2(8<MhI&V+9hR1oY#$r= z=m<$lq(m6M6bB{XixS42xYe7ca-Xif)}DK5@brn#zK1LEM1;z}d20}49CM;etj$>> zrJ|u6lB5)&;`c`B?0T5hdCl%q(_H_%;V>Tl;nZ36YT|m|!os5H^Z7tgyfi)7ZBngL zDjLe+w$8TEfoFQh?uKU-63glOkyh8!2Ok?-zLk!S|8X%qdtL2LBo^CIHukxK(+59S zr0I@OTYT4^o>&9ra04tWr`0cqdu;S6lD5BpcNBj{Hw3%~PAyXN9+Cdni{+F)fHRql xB1zJw6K1nn_Q6d#9Z4pWLumio7yiEj3;<I+Z84p8DK`KB002ovPDHLkV1hi8$cq2~ literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/playbutton_n.png new file mode 100644 index 0000000000000000000000000000000000000000..24840940bab522e7e500f7a89d53fdff27706ec9 GIT binary patch literal 1266 zcmV<O1P%L%P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$lu1NERCwCNmP>CFR}{z3JnivgX7D4g zil`+9i7siYN?nmn7NKcO=nf&#Z$Q-zJ0v~;?Vbe!)J16;l2)xK0z^06Q&mk$BZWmu z>?kqz*b{rk^XNG@*Jxrp*sv(qx*B=r-1*(}`ri@4Fogemi0~H1TY<{mHwy2**JCtW zJZs{mh;v?5)YEu1`J<}ab|2$)1?TcF%Xh1Tfa}jTK<LhCHxz-FWJ$ha9x=^qHVcVl z5@N9!2>5ed7i`;x-Q68{QF&4I1OMvJcbC4wW#Jn`5qQHejEhH)PC_o1g;XlVfRZGE zAPC_5KD6C7*n2kY?Cip`jSZ+-wc7~(0+$E<-~^Qq+Yte?ne4@>LILvm9O$|ZT09PF zOa)0KD1b-Mb{o=83X&LsR4NIpt7{j|oLvw(*y{(D4*_0Er*#G#A0GqLG$ENtKs+7; zS(ZT*MdoP`1mJldI2dNkHG4+8`Qyox^K%QI|M}C>;x&9-yBjR_D;iM{uS`x(G7kxu zPN$d`2?YQ9d<(A{Xj+{8PbL$PLNCe2Gz`e+a&YX}6kyse&(0S|xnshhmMQ9nVRk}F z9ws!6c^SuiK79BH?mu`0s;c5tAg1o$*U%<`8%U?qFgiK{M=}{hRuroESU0C1be^h| ziiUDXFb1ITDf`)2|7RV(yR*nbOCePhg>8VraSi1+pU*?!2eW9dc6-|odRjMhR;5%l z?4>v*Sz-m$8CzQ=xOwYaC~t4GXJog(0hz7MWHOk@@DmPvH57Z&?6d_zSkZ_AD;%29 z(P%Va@rNaN`g9$z4$xj?>tvkl9ipUjh8$(}K&c`d^#*Hv3Lo`xw3X@k0b2wwudKjd zYkxz4hSa+*wh`UuwzN|z%IayEAPlOOWtq0U2Q9~8FNpab=0}16z1iL__vGW$sSn^p zVG2cJf6X3V>-9Q?*WyrNP@g_-Hus7S0nuA(e$Vs4lYI~+>3|`HaAsx(KK|%K1|wS+ z6{^*8z_My2mA?-q_$1y|%_>rOXvfrtDpn`uHabS~BXI8A0=zRZj?r*|Hc8-Sv&r-r zwHP-k{>p_R90Xl<T<7a@xs1K8W6Q)}YHi5#dGh4@@Y&oM)*b|MSV)^JkF}bGmHiy= zw=ArUdmQL<E$&}PbUY#8YPnQ`QmG7;N(E}w8s?}0^@a`8)6+2b=`1TS${~U31jBGv z&^z2LY;JDSMz8($;J(FSUKD!RDI=h0-&ADfLgB=57#k)1HJCcF-j!rl8mv+=hZIus zkbrAztKhZ0JE*%~axWd6HcprR4?5Z;0^^R)PfSd}*w{GJR|+>N5DkJ-(NGR4q~sy0 zP!vcWHu?02IZvs~Wjrmu!o&Hp7S~XbO~CV&=`4*_tHr96iiUDXA^maj`|FWBwm9(R z5jV`21A!GfEl9jcOG(;rjm{nlf<<jXRf>7P$8*^6(BaSnN83=JD;<ydX`x0WE!y;* zw&)a%%b`7o_$$90A|s4cUppe;QWw<cc3#9j99A3-1N#xM*r(ls2q=85PtYOYVYKJo cWc(+<0LyMcaH?Xq`~Uy|07*qoM6N<$g6RWEAOHXW literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_d.png new file mode 100644 index 0000000000000000000000000000000000000000..ad03e6c93956899c54ee34e2183bccee810e6e2c GIT binary patch literal 701 zcmV;u0z&<XP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!Ur9tkRCwCFR?TY@Q4s$nyYFqbZDIpr z2`B_ADr!;iAbJr0g`Cub=s^@I;z2zWrS#-Qdhn8eBYG23Td7g8O)kZXwxWB`l+ZTY zCfy{q*^O@=Huyp53)$_!FwDp9Z|CjIya}b#>C*r*f|Ce<LZJ{eO@prM;EY?0vy@JN zrooeggNG51#}N*P5fqintI3tJogq*Ocyee6(O67=QBc%uHZhUSCS_WbVbFj?^nS(| zBq$we(16>-4Feq`jgLLWySD|b*%kE1V>oyI4DOBHReqj{<T?T!86L&l+$RhS>_;MT z0FG0`<jXfwjy@Stt7dVk%BA{wegTF22OK(>fEh91xvrF?=cZ?+UzL<WXL1FYra_Gd z*K4(zbh49Qxu8@7`=Y&YT@P;4{m<9>?e`t@_^D%PG=8GqXn=Fpwr_53q2|=3-}mD^ zdg2JK-MEBOX%*F~gL<uj%KC=X$xeRXGbOru>k7Vou`vDWecN{7;#q0$gp@Na6-&5z z`3`cg^P+Gfh>Cwml610@-&at2;r-i>@Z)<4{rmdR(`$;!!lfjg?8Da|C<7~jK7Kfk zm1P@dPXt~|w2J7_Z6=-U&PGkf`2sqV%j4txA|i4icsnxrDURa127U273r*MHdEQ?c z{>$X2IEwGOdKVYIid%@LkhgmVB97v_23;yHL#!Bb8mSP)`_iXdV(R#MneP@bG5gH- zi#)7p!7XAtg6f*S_n_>L8)MHv4Q^QWnl0DEplPXCBw^`{^VE8!nwgrKOftsB7S3do ji<KY<TuX53mjD9*xDN|hLwQlg00000NkvXXu0mjfK~_#r literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_h.png new file mode 100644 index 0000000000000000000000000000000000000000..fae070bdf311a9acfcd1331932d2a16edd731315 GIT binary patch literal 795 zcmV+$1LXXPP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!y-7qtRCwCFR!vM(K@gs{5BeThT_Y(q zLd3{HBoU$}HYWbuJaX;9gy=;s#ETjZqA_sspo!t2S8rUsd(fZ(6r-N03bY`|kL9-y z+TC@(wGd0_zDHua$#gqA-_G}DX8!_XA4g+Elu*K`{4Jwo(2`On8jdxvy82kz85ibe zS$aC104Y0M7bPh~*-62ZN~f45ge1Z9Bm?Eot&~LIL!|1Dpg@I4!Dk_@n$+jS`?ve9 zCX<tGZg!K|PB_!teEe(wv)(tpNFj<V$kh+$+?RCUxYsd0K5-=)J;GW}Hxqj3ce;A7 z`Sk(5piZw+gfPqK&={Le&9yf+93{sN>pAG5e@{-uVfXTZIp7OI@$-elZ$C6SPMA=9 zt`@_#K*u=je*Az>uU;r#HFauimvdE$Jm0fc+P;TvHx7J3p=&C1u1(U~-ZJaDo0N53 zrSlzuzOkOwb}t{`0iPublf5E6bG|im@zS|h!|y+LFcu*z6rx-%Ps=N7gmKuBG4DwC zkzTZ$-PgbTocUG%<6A1eJ$B+`;|RvR2#eCoeJ-S_iL`yU>tW~Q#7x|AY>Gr`2|aX- z!|rd(aJ6L$k^Wn!-IAW1wx9Gp@7!2-o7kZPR8#9vb+}sh&@uk_!N4uxOn$&4|3)hG z{xN;si>EI;w%o0lTA?JQEZzc2*W+9;;Eda4=Wr+-gM#lZ?_Q6@7UqASvh{&b8V5UY zz?Z}+9;sKa{)&9b;*Sh{Y71E*J;AF12mFQ0XJ<=cz-Q@n85}OmFEs^&0V8I+0SCMZ z3%<gAS{k&zmW@?aSLu!IrvY#Br|(lysHv&twsQNrH%<QZi6o^krmlaW=h1#IYm-0l z>GW3OeN>!Yk4XPJDI&!QAu32J^}~#0W@UMqMIr~wPPn?dLg2$|aBy*HF`=YlFZ@3O Z3;-#dcQ1~s5H$b*002ovPDHLkV1lCkdk+8r literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/player_n.png new file mode 100644 index 0000000000000000000000000000000000000000..15e7e55046085eabc6a4ccabaea665b5e07765f8 GIT binary patch literal 659 zcmV;E0&M+>P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!HAzH4RCwC#R!?gaK@@*8*`3`@B34j? zpk)L8ITTE8f?fh1y?FPKB6t<_3)t`AQK5SE;wKQlLW7_nl`QBbjn=g3CfnVe8Q%+I zskQB9H>n=<!Q<_leaw&fy?MV~rnQDYZ5;lB{Rcf!v$6^<`}JRP|6F4QLz^Da1N4_c zzq4}O9_v9jALs$VRmT;}wc1&zRLT&BBfr=C`lNooai+V`d5k3rUC)hP1oS!Q%a<-* z0MB(mNjZs>GsYiYS!`T0hhX#UY0L8*+>(J*de14Rb(O+xtZ~sxGxv0#4Wv{cQ~BEq z;*)dO!648Rba@=jc|g~qXcxv&1VY$*i*cO5C>(>Rs`rLDZYb6>(~CDWz^(50df#9; z3}HNqV0&u^D7PH;=wtWY=M$OJV59Q_w0in|a|`<YO&AQev8C$S`j@p==43L9+FbPF z?Onj|n(<Q0MS3l(1S$4+H%=bZYmFsdj@zHQABI?yW`Xh?fNvc~v=&ZRp<MAmIARhh z7q+-5L-FRSf4w@-IbF4FtLc?pQ1~g7IHNzEa$2`7YcUzK=U8iJ?aQY)Ws@;>*PRH0 zI@0MqXm$rh;)Vfcv-GrG!u~0xG7G6r#_2s8FC&RbQP39Wq(a%CREKx#)&Y@FML?VM zM?-2fl_^*<Qb>Epg-0fdlOo?~%S4$Q6qSe>x=x`xQ-ZR6a#&a#v>Ez5cBDv53rZA? ta>}MUjM_Y)>abf_)Yw1sv;9MW0RZ#=;Kl``AfW&N002ovPDHLkV1hk_F|_~y literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..1e9abf699350a6be240a405e48351185194d06ce GIT binary patch literal 1227 zcmV;+1T_1JP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$ZAnByRCwCFR&7jMRTzGHZ|_GbEp+3f z5WvgFVA(($W5V1=qS0+Ja~h`8ADZY7f9$6}{GlNx{&DILzmkbAnHr5oU1IPfnOpW1 zWg8$cm|;LRK01)n(zOLjOWWJ_`kqsXa|5B8`re$J+vM~-_kGUuKJPKLwY9kbwA0X# zgD{y)5JeGYvl+4^yCqp_F*87mB5Sp^b#OQw5Cj3aieNCH-3*4>87K#AY-)ndZs%W= zt4Jo3ICJ{6#?xXM<{FR%+h39-a8O2Rt^t=rRul;MpF^VL)}7wy*c>rp!r6`+Sd8lM z2P3Gdbl^Uh6RxJ>^{bh&y57aFz1x8=j_ikaS0n!&2rS{dpGNWOk&A3AG&hvsnFCu< zt=ZSyYhCpA_509uBZQtl4=FIkhutpW*@F%E=Dodm;2tNw>YTviFP}i`n;jUNnEUUb zKb*XlnNMAZp45>}ry+;}FM3fBap?Ibyz+Dt6~Bm6!#*5({T%uRynNli2E8yegQxD- zaB0X3UDuIHry!8i$g)HwKu#mecw*lsRN56N78y~KfWx2kA{LL+M<%leWdfRwCGp5} z&6tg+Fg`xVi#(OeWWmafDKZi@;EU+U&y)G4b15LU6hk7BptY&&Ks$S;QCePz%l<f= z<qq^R+6eGg>O!FyUV7^nl$6@JG7D@f3RID^l1S)W1AjA#wLoWlVWd<U!@ekzs)Sj8 zl+&9wGQd|yLU{YLJ|ry)tS}IY>2NyjsJ0lih6b%QXwE9r1CMS-czhO}$2$=o^I`Xc zRkV>o=DmauKmQFczJCFQ1fr0L7Q%5f)D&aEzkq}Ls(FWD&mDHS7HC~<A-+7k7ZdIv ziZ)8H+06L*#|d<e&XZ^qJhY<*!`J;t)7frwRbcR{2M@c7VYf+KY|LIovzQ$yi%qo- zR8?5<{mD`MJa`jORcxs*fmIQ4WyBBFs-WCqfoCcRhYUQnr;2CE0L`Rs@wRrku9BIW zj-d6OU(xT0L!#_zOY$I)I4)hD=I57=CQw|cVrVji7oM#DOVyH;=_=WRcJ_MF`au`8 z&1LX<=TTRd507^N9alWqwXGThMCaZ0Md<3E#?~?`%1SMq)@49(g-gLdf)0)Z(fV-@ zb~S5cg;S_0&8KoPFyIcMv3esW-2o)%cZJgi&wLD%!5E22=V!QV9749BZ+zN|iVb-% z^b~%luCjZ3CC(D1ln6H1RfHE~{5=qeV2`UDk)(lR-;PqI0=GwIlUdu~>|z217704l z9;3`kbsEz%78s**WZAO!6J3+od2bnNit;fqwTNTKuVrd^74J12J^GQcbNdc#X>43^ zK|G#7bZH3*^3n8dMZQ|FbCeYss-kk5Z>6}qyPH%ku3a^(c{Ax}8_czg6+YBV_s}&i zQzBG)(o#D6`!4;L&3Ejn$cj5O7t&bgSm|=cd*&*F$Tns?9yYltQ@Le+A>4lc{5egM pWNO6HpSESn#l6f;qva0)1^^W%uHR8zW>^3K002ovPDHLkV1g~oL}UN} literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..2d57d4a10dcacda1f4f88aab85b2ba8876e5d537 GIT binary patch literal 1424 zcmV;B1#kL^P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%GD$>1RCwCFR$FgWRTN(5%sFQ+owjpk zrk$2fheBJRa;<<sVnU5dqCOZCCCHmX_y;CF`akr=2NR4jDTxuHG#GAbYoV>sE1kA< z%5?19+PR+F-mZ0~4N^`cnv|2Qoc*x(`qui^THoHn$o=~*0B{a10NelN(Jo5_7>Psz z;YeiL5!0zuAeBm;lNe_KH53AFncEIZILYN?K;j(0vMk^)fm?<~cnHdYF$+Ktw;z-< zAUugP2FyZuk!`to=Fv(>bcry0e|Ep78J=RX)EEw{sjkkJc<-V1#0!Hm4umH$0>w<( z)|l|^cLN`M{@I6L#iG48?S@DfeYbv{KGuKbm$Us>0NSH%GkvEI-tCB~nSTdn;JSG6 zLSIl-?X;b2WNayV`_AC$=H}M88gLg)(-dS`Fn4!^7Ec`S9$d-l^&_KW$A^Z;Ut35N zB5Gs(a$|$LvX#F~HiM)hvSnxMY(w{EdMUzT{8y`0kw8U3-~yV<<znx?-E;kozEeL- zk_7R1yyxq0u3Z{=kcd%pZuYf-BNES+Y1$p^WRK?;x=)-u3Jdd#-Q`NfrR#=32PFh_ zi6T>E*?Rp%$L|dd4MoM{#tvo5PXj|A6^cbk(=_23e~3ePA}3Ia!T~V8kPTI}O8u!f zkAkLG{ELgZa0P)4!w@zyp^PgUQ_AI%-{bQFvH*r@_*Ylg<WiY})Sm+5e!@d>u<VX< zn$wBNC!K0@2<B56P$Nwg#IELMU&b)p0#a-5SuJSZ%irJqRBZ}^X_(;i*MTUCI<Z1y zNhNS&U{A-!qLJZXTFA5DAe8xH0J{d3SMnf8E+`ht0i;*dF;iKQVRkMTy>WZ&eYZ~u z(|$uUAlRgW3@}Y>7?LE|jVHO*0m05xv>0XTT+d^#b`0m1Qu^49LC7XjdRJ#~6zd}7 zSIfSu*C)>WaP97erj}5c0w|Zt&=%VPYq>SOr(3;=EOQbA_7C4S4w}T)YwD0Fw6*#( z=RZF4<#H<D<nsyzS?1d4c(yN<$sh81J<5?|2e8qe03F`9zYFGO9z%3T_zxtfDuBSL z$wYw$q$UD<T_8{fNs&kf1;=4V;B+V`=kARqPcEkmCl#;a-`&{`Zp<zoPk_hg0X0w$ zE6W*hiLPYlPVa9UwZz&o%Glaa9(z_<t$PWbS<KN=E2Yqn1Jj>qOnA%VbNgGuDhRlL z{J|p#;o8)6Xsq|a(qa-`*%Q3V*E?EKWJ|_OpYd(WAg_r-O$ML0sd%<~;MVx1&OPmI z>6I+(hy+mr^YHM&3>-YN7iOpCVArlTn3$M@U{FnB(s=@uCCS1}Ez0eAza$PsFWoeo zY+<Poy?J}|{E^<h%Jk$sv_|mGMV1!k6A<f&!e7a>-5lXiBjj_dfR{i>)47KzRTRY} zdbxvNX47k5ZO_Z!ox!QIYA}EW;gHQ_q4#hXjEqj&P1AsMmdjOpU05r@;luk#5R{48 zwfA6MBFUWm5?)Pv9mWN9w!W^;YY)62ir{t2=(x7lsv2~}TJ81F@IyGz6NBcaFr+iN zoqtZPzGE0V&8MSZTg9&Q_kR(MMjbmBML<N_lxkIz@PP0lccvsu%<J)(9*^5}D{gD^ zP$UuwlEE6+P9IT$R|=OTmnE&M%8Pt-j)FuXKr%%B?CSyN6nKqmr;qA_Q5%Y^2><nI zB4ft2?%m{@vzj8Yt)C5?B1QXuZz2c3wjz<P7mLL}U0vO_Kg>#{l6{ZR8|iXBmpg~{ e(q8y~3ormjUirM(_&$vQ0000<MNUMnLSTaKQn7Ua literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/river-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e7cb27a0927e3c92ff8899512302ebdd86af9b20 GIT binary patch literal 886 zcmV-+1Bv{JP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#7)eAyRCwBA{Qv(y1HAzg!vKI$OKA#N zh*9+J-Fq=cMn;CS=Py`(`SO+f#mg6Bswyh?wKO#@OG=2}BiU9^>Lr8=i%Z*(`QN|) z;NH9cpnp<w+T6tC)VYh7u1I<J{(~4XW`T1HAq9`0JXJC>Fx(8HvB|IBxFyQU(rUAv zo&DxnbLJHgV>!V%-L-qK6Ecks0{py}^-Y{Kft{U$VeRVG&e_>Hlb*kLsYFpi+PZnO zWpa8Zn8u~X+|+olvWn^}9v&WG(0}J{ZEde6+fq;hdimnHn5Tyu1BeFkv8e;a@zW=d z#rOpT8JL-v7(RT!u8t^6uidz*tE8gBaQEJQ1{F0mFdtpbj~_p|n_61i<P{Vdn3xzD zghfOcgoK2?lAUFqJ$ok3!otFE_wGHQflOdNvi!@JuaqWEo|4bR$fU%>3l3TaX&D&? zO--#mWc$>`$#Kv9dw0LCTeF%0M1%M+-|g77H>#kpXrhFKgc3UkJ1{EVG3e>*GrWKI z{;RLI=W=}JG7_p~K=JtI-8*p+A)%LCTwGrd9Xaa1XU`r#ZZ2+aO)X7^3l}bcvw@+J z5yQz7Cl~?(0_NCSTW+MtQW!XU{-QI`vM5GICNWuASq5%yE{4;mPcv|Gax%yOgY^0H z7Yx8s>vCC1VHL&2;D?VN#TG7FS^zX$myL~$L0(>-;n%O<414$PWzf;lVR-uVDTAn} zD8sg`TN!4|m^Ojp0{_9oN4jn8os(2mRdgRdc)*~ls>bl-$rFZMJ9jX+ySp<SJ9?DC z*w}<&|Ni|9re@}s1^D@2QDiA7WmZ?$w7I*vb00l=j6p+F0~mMz!0~2mV#;t2n2mn@ z`o$n6CCTs<=#bmDZYhBh7)6%$^z~QED=0Ah16uUp{yhd8Yb%D$n>K;s4H%pv3?JUV z2eUuC|G?lM7|8JX^C#{Fi<cBo=F=B1#DJNc;rH*~49v{T3>+LBz^w8EoL|3v`_5ow zYy`BFiDB*9H4LUewwk&I!_mV>EH-S~nnK7@#?gk^sHH>JQh)#h05lB72<rcyPyhe` M07*qoM6N<$f<2Y04*&oF literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a0e961fc9e0a9cd1d8e05a1b0812eb6528e8b44d GIT binary patch literal 977 zcmV;?11|iDP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#b4f%&RCwCFR&7XAQ5b&i-TiQu)+&%7 zXl0tkB9k)uP_q)58M!eE7TJQn1Cja@{Rk=|kRm9RB7(vsC{Q9S(#SA0Cl%F?3L;um z3Vo<)lYOuE9H*65yF=H*&hEK)XZJbJbKdtoN2so@&IC|T&4@Ta5CrISI!KZPMOIw0 zB3mRzs8<zZYU&&q3<ijzh<Ks9`>XLwcTYVd<%FgM3y_qY%x4rY1OfrvZE7-dSxjNP z2^rY?WmyI%WtPU9a0rU3Ld1WDOyXiDt*JT%^Wu3}I(r&cq)(4MnwhIw2fupu1pBtw z;CA;7r-F6UW_Uba-j^gDPYq3Lt7TBv`*$ec;Xr?1FTd9(O~L64moY2Dgn@wp9uI<$ zFutVQEQRRp`7)Tp0#H=E9y3zs!te7zM+!wzAln4Z7HeA5_a8oD#j;G~ugXTh+k;}e z1EEle6RH%AY67W@p{QsYnKU5*tq-4~`F;oW_1M34BUdd;GAHE`Io_md;v~$UHxmwr z6}dTC_|W+g2X~d?>!&V=f)H~AYa-3dUyYY9ToB35wDj4qS=QibM=MG<n9*>pmRlWD z+R!c&&dbTce3R*)>Ef^!A{-8b>!L5}4);@1W}xoDV@=WPbUL*~Z^ptbEYDqwww5OB z-fgER;XyX~ym|c=4YwX(-LgeEbnFCH7MP>1ujbXa+Dm!l_J4Pi%@Vg-BxPxHQ?X~S z9b&K-2lj49?fFUsgTb-qDFai$BKJzo4PJqM1-G4T*5p9d$)kAr{4tK4y~xw(PpUPO z9xmGnpU;o7@~u$y>Q9SD;AY)zG~Q{1?U56vtZcB&Yb3EId(TxIL*4bOFc+;v;>1KA zFgj6+Ty^}VyicvfR*cCLfU1h)@Or(paR*~xQ$|>Mx&qdcd}J-jgv5!&!2-|7=qg1B z`MplW>Vh1!x3$3M_aP7rj7q7f_Fg)F1``t#(Ae0<8&8$UnhvdH<c9qac?L{1Oh%AY z`u%>I6dlTxpZX*=vwxxe&1d-OY~%HK;ECA9UaSQn8pnu7DP^;WhHxlMe+*N}qk1dH zR`!_vBcolSb7=HGk{i6R%W`bxj>v{K_}@rXMRoP`_88gm@Nb8+p~xI7x;i`A#YIXL z%eTJ%`sU_)Mp;(q6fSd1{*;5`02ejO4*>=M+-MG7*NL1s00000NkvXXu0mjf9`?*% literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..25b47ae25d15be6c893215d1c06c33a411cafcd4 GIT binary patch literal 1148 zcmV-?1cUpDP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$9!W$&RCwCFmVanmMG(j5?e5*XAHiN* z;R<5gOPe6JHJ1h&u+YR7rKxC9kX|KL#A?MqiXcS#k4a5~t%zc&LW3Gqgdq5bqR>EM zYty9Ks7a}yRok>_NOK^`#oi^CySKZ}yxcWEymz?>*M;FN%iI0z%zkHfje-9DRR9n{ z3qbK(46R)dFc=I5?hXcvPW(F>4Md~Sh)`Mumakk1T3SVi66faV&4ADvKuQVv4#ZZW z5f7mnl#&1jD?U^!AfC`lfs%-4YSBq=w;bNkbN0;k>E!eZ#tiG5(>*Oqf+`Kf6N;ed zsmjB=(9^x_Saaik%d$%B0R!NH%Bt`FxfSOK*MbWz1lNUu5sGZ|R6722ZSacc=#e)f z=~T)F8eGH<zWUnd8!9T}<6~oPkH?fgpU+~3yC5j>Y)>Vc;qX(#!Ixi<=@f=@=FB%$ zgnuqAzi(=CYKprAiXT_Oxmqbz05Ibz#aUVw7)EYvY~t36nr+qKa=D<hs~aA9?1|q} z>9k>*ru2F}me*rau4$PkTDPILXf-fRQ1TWh<+2i=2Y%`sfb+e5*!RNmV@K-IYi+vS z62TU3*7nkA{yf9~h{u$cwxX1SNo~Z+%0sXx@+54j-i)441m1l8(9V(JA(M8k^2}Xm zHV1jOr<Qd7v8=rO;-7z91;!bat_#7gx*ae&GQ6?rP{Z@>pSFeM5}?E(p3&9S*%VqE zay*BNzw|A8=V;3-nM}DYh-}~M^^Klr`>d6-!cu1N+F(XVdDOcpTs3jey=#6PxpBSv z+2{7!Vz5PrcIB_DaJuW<su%0`9(d~M`mZ0ZdvwG}khAI~A!%jjsm!EKF5C9dj)BRk zB*fzh;9LM>92l0(^=0dTHd5ku1wZ+y^_h=PesDi2+i4x*5YOm1b*gFg>NSgo<)wXl z8YwB+0#CAI6ai{$OV{@{G&XmV!i^m98ydPkTL$yBx0)MkQ>irUKd=wnrn%r$XIsa& z9dP;b?^PFid-^wTsU8zpJd7M=kWuBQ-8xz8iTA?4Ub<9MyL~HoF;6+Fm~)=_GN4M8 z?eBWL4wm2LH^2Jwi<-pbq)4Sw?7wR&I#cId`|$na;d5ud-@UW07Q&SkAP`39nf-Dy zKJ#T6+gkG=4Br^qh;@?NP$}o<s?OG4hZ5G`_rq?gNZn`7+eSgnp6T54%_^G4Xr#pN zn<ii~iT*RDD9rP3namxRtG#=&?XV@KkxV897I;@WZQ;(9QAj@0JOX3QHaMGoAP`Sv zW1Z*zGbDmgi5hPe38L^zn{lq}+B|1{>I={_vCelnn5Nqj1YBan;Ogv!Fvr!IinJJ% za*nb8|Av~oKJh46v3NWl@caEmf0!i_3HypB^|r^x$0KNW?1lfY00RIlnjLc+b^x~k O0000<MNUMnLSTaUBtr`T literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/road-path-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..9435692d9475bb61bc3d691897f54d6b9352b786 GIT binary patch literal 709 zcmV;$0y_PPP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!XGugsRCwC#R!wLVK@@&7yV-Q9zdb~^ z7?OZD8$vA+u{D>fAYzraU==*-)#BBQ)T4sn*@Fk~9=r$=y@^usqTY(`HZfuk9t1<O zyR++?@HVWaNq1z`gFbj<W_D)3yl=kmB`gd>_|w7RFT{V)-x~U`hX;$LpXSzq{Bg$E zWyaZloO+)eXCW!jn*A{uXbdceV7Ix($7m@OzIYfrFXUP(mCDFj)~FEvjf`Q8NqhO8 zx52q~^K-ND6(5_JJ^_l<Ks8OjtLd86Kaj~hL0Z4zFcYgVOjSr&rFCec43){l;C8#< zcDgU*cOgKRGE7ZKGO{Bh@c!M~{x}GN6&zkCT0P7lb<1=+osE3H0H@EKg^6+nJlD<b z8y~-lX{uJJ;?sJg;Q;3h3dI5(o2lVgtD&gwAaDhrGgVMFtl>+aO1q$t?hkr-Ac7CA z#`^MhRmXk0EhyRMijmFEou6L>xgR~-OUJfh_0>zfXJO;EdtX|uwL#!<(4=j!E|uGE ze+OOH(MB3l%%NB+!nyec*u7_O5%=6ex~G%Sov2b7`BLd|RAoG(wdg%a_amhH!Djy| zw?U-$lJEJjcwq?)!+@>8WjudYhvvF-3U%-lJ64PA6e_D10cY{!;#Bnrn5NmIHcCmc z%xFG);w0?YxoZ^fc!@d|SGEiRE+3tqL64UEK=NMWU~%xNYE?2(BT#LtJ040eD)ySB z@y+YiUiL&lw8kmke6RT9&?Z2g+NDSg+O+LCV#A=+6&sIwrIErZL+Z6EA%3ahlL9?z r@G21w0@f2A_94Y!|IDB59|8;j(FNoamoa-q00000NkvXXu0mjfZuw8j literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_d.png new file mode 100644 index 0000000000000000000000000000000000000000..72457d069119a7920ed5184200b0c4b168a8b116 GIT binary patch literal 936 zcmV;Z16TZsP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#N=ZaPRCwCFmTgE=VHC&z+wQ%ynwnJ0 zvf`FaN*bkEE$vMUD=KB+ho~qb3aRkrL-eUnR#a%I(ZJp?(>Y9=F-ys%mM=B4$r3BE z+R#!{+oplum5uK1ObCmn<9gumJoi4&y}xtL^Ky<rrBbZ|pk<W?4<Zl<AQFi{5CkZa zvXB(1BwWC0Vu}t9UW$N#00@Ob@FiC3SGwP7)4HG>aD`kBUq3(2BEDo~WCZ%kN}6-a z<ssidm$?2<k|a2&E2Mk_e+wlRLx?FBl67vv(6ss#eCThT{BCBWIw7d((+f2By~TB7 zhF9KB2wL(cA4{ltXtQ@h{Zhq0)M&1QvG*PJg&sx1qIjs=?&Gu7%rErXiz|>zmLi;v zLd1d%2qgS)_1Q)KpbuYZ&^c&@Z00hqpY$*l9RoHPzqFyK;U+hytV!efdG%zHrnwLj zCkb^+Dfhf>Q6v`6S`6L0GH6Ui=(hJj_d$=@l0YbyZSfvj`aB<<tXH}FZ^2|~gt_k{ zQudtT#$s10aU%aPv~M57j}#-jDg&~4p_t__^)9@HhC;*yDL@Cx5U<!j2IQ_!-`Q9& zeIe!&zBsq*JO+k_P*j`i-I^BH+(C!kj=hRQa5|mi#w!f>QEN6KDLNJM;5FD45{ta1 z2QalaP99oNug2#2t1-tv2oA^hKgRm3JxDLl!a8Xv)`q&*@p#;61kaE{^(bp{P|f38 zoR@K72^eM=Fsu#_Gh^d9x-5)*cjDyEQ()NlKiIV8l4DyF(e&v(Zj@$D9GX|7Mx;~@ ze~Hu$90fCs12v81xM$Sk$l8POp(J1&4mcP$cvJXx#4nFVUfn(P_IG=}d++LU&|$M9 znx<iFtp`E6^B~14NfF4@Wn;N-5W+(?az?nzlF^TSe2^HIj0)3JTq;OIda}whXs%w3 zpKR!E8%q8eqL_$iG_};S2C;^)M!6HTyCnz_Vr1)4(p-%l^<~%=wfq0jCs|Mb3Nkc5 z!-2~ux&F<DVvla0vjrE%Lq#Ib6V1A?O=!RS+UrCvBt%nFru+eA>(Hr(iYc+hX18(W zA>Y#7)#Yj|1W8fKfx&Ou>gp<*Bq{D#h}hl79s)kN7FLzN1Q-CAGa~r;0-b{Z0000< KMNUMnLSTXxptk@3 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_h.png new file mode 100644 index 0000000000000000000000000000000000000000..7bff1444ff9cf88ee64e447e2cc092842138986b GIT binary patch literal 1102 zcmV-U1hM;xP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#@JU2LRCwCFmS0R$R~*N`t-YnDIBx~g z!em1*sfgOL5L&mHJF^TnlSyWlNi>Uz$#mJ?mn_SY?P*VYSh6MaW!Z}nP22>=X85DR z1gX%J7NxUj>VK^wtpnQI*>lg%xfiIgmco*9l5cYFx#ynG`TfrC_d7>7;`7-6G&70F ze~U4-7&y>swTi`7Yu<?yk%$<HM4Al>RYXfs5hzLJ9m+Wm9zGBalmdhh&{goQ!o+zD zN}(tO(CPCJr3yGtPZb3U;b0}Fe(L$*Q1G_jww$pU#W<X2&^idy(yR1`hby8_qZPeZ zdYY<i)yMv~wG5<ZY_<ZDQ{HipWHDRt_tccFZSd3^%lRjhI8V>XB!^qP<AW8GvB`?v z&PLSR-3Z;jCf#}z7FX)tISzwTkW@;I;B2}W=xr*sl%ZzR4%o}LqOhP4XFcsrOZ}5k zjKg^>kf^$XYy-Q7es{*>3Gv0^a$Feh!jGrF$CKzJxaGeM{Nm<gI)^%(%Yrfv=dt2I z)QHNKr&n$Sq&2!V=<{As-?vq5M`>{x21f@F439~%si*7jJ-*{!w|0H3&hEOIJ8cNd zQ$|h^=4ai12Xqbn;hc%b#m^3ZflxSz`;YJA!@Y;qZ*IpfeEah^rknS|?gC>0F8X?# zHkEEZW+}AD*?~|k)f0tVn%NzrA-_~pxeevEm+|J#x95RsNqb=fHmoaxneFdK`#wf0 zMa0g54);>-RXI#rAZ3WOv;FO=l?^-hsPBtg-{o@%-U#CTh7Vw`tU-N^3!z&fDKI`_ zTRM7qAW=olA2QoHC;zp8WPfQ3v#1!!<Ub35Pofhz(S972WmRxg*QtF6_8vkJ8+*>v z-MDzJIjMO{6)k!>T_MW6I(pAI<B7Pq>y_O|$TDQ6L?XRaCmAsHj9~wsgOFMOvy^s1 zz4z8W@TzsXv&H@VeDhMMwx{Vbm{#DgfK*vpg+jBa0cXLooW!N!KhZzbkA_#@fl*is zIhjOK*1)dX*WlXj!r1koG!c7bT0E~NUVFKFX*yxrdwykk85c`LD@MnB5Cj2&QGk(& zKc8$njuNp1TQ@t@MewGZi+qy_``<kPkI!Q|)p4TnNb{$ynNFJ7qb(gMqs&UeTz7u3 zJ8Q1(sINufs#jfvK`=lt80Mc1?Afu!iA&xAXW!-X{@3fI1-Z+4y0*5~uQqNh&v`nG zT=m*oyU!e5$%$eVjIqx@`SQEx`|jSoqZJ35>s({aQC<6x>FbpqWsB#YErWEyp637d zz@VT?#^ldiqw39pt}2xC_`NqaJv}X&&E~uVXJ%&93pc+bwZx*)W~NoU;r}bZ0CSJc UwaM;z^#A|>07*qoM6N<$f?l#JR{#J2 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/sculpt-terrain_n.png new file mode 100644 index 0000000000000000000000000000000000000000..6a8533f5aff2af58200679d7d0e379261509d216 GIT binary patch literal 751 zcmV<L0ucR)P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!kx4{BRCwBA{Qv(y1HAzg!vKI$O9zjo zlcr2x1H{^Nv$TKW6sx~~|AO!Yx>*XetVlpm07Pr|^i3?Lxuxx$JwPA+v9h<dVK6o} z0$Tj17$_!7Q%ixq>lYIjXHZg9U{Fz3V<D0*lei(#EFN@V|fl$Vy2{GMqSdhGE{k z1q|=sy<-51SpnrzL2Bw7o5M-5l#x&|SX<wClAVoR_UEskV7``?HiLwOIK!z^ry0I{ z`TPl}e<dR$V=9miX=!RWMzN)p)wLiW^cUpiGTga$pW*rQ7Yrdm{$RN;U%r5{5X0xs zpBUKL*%^NR{Q3C*zyGcs?Jb`u3f|wpeix~$YcPn5i!oSRn!_yw0UjP61|cCq1}-ix zhWNNx1|}wE**|~&q*4^8g+-;|z{Dc!?Bu}k@83TR`ID#5GTgjzlOe$0pFvSUp25V# z7?ik*3kplL$+i^e>LL|YRR$hzZeY;;LXrFU=@Y~1RjV1~6%-gWG}ORif&RV>Lc&5I ziyO(ZG&3tV9B8Smg@rl8_a8qPegN_J??2%3Yu9ZAIf@}D(4XN4Q2aNL4FZ0?z94a{ zjLe)=k}du7=Xa5Uq9Owa2Rp<<sKq~i{A4(L{vyNiV@DaBoSlI2$i(pD=TC;8KOh$C z>*+8Uo0xzLkK~keK3tZvpbI1>rGQebtgx^!!_Aww7+6_ZftZbfjg1w|Ub|*3gP52& zgR+tm*b(3?zzEgM!NCw56u@xi^l837e|~_1xsa$V6C0aw5@-tEBF@g<jzLgR2<QkF zutQi_SfGxBu+N@7%W&fO35Ngw|Aj1Byzm&wg#!#kL`G`^3xVAvmUxesFI!?xwxy%Z hl~GFvouvQ)1_0jI7tQBjYVZI6002ovPDHLkV1j2MTs;5) literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_d.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1bfff7fea6e3f5549213ccc69ac11943a89564 GIT binary patch literal 1237 zcmV;`1S<Q9P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$cS%G+RCwCFmR(GnRT#(reZSgL#wbvb z^06)yw-8zQn9A5j40Ddeo3V)TV<E;1HC`Cw)e>W3{D{Vz-LVVJ5~oYNFyq3gL=X&N zW7Dw>*yi}yC<R(t`bAqQZ~OLrJ?GF869a8c>Pb#c-=y#PJ^%lC&htJ(UteDnfPOM< z$ivX-bl`a&f*?Q=C5I@9-2w#~^^&cw&JMHL436WFuMr6NZN5OTpF$b%iN;3kDJo(S z<!dO4f}z1d8<Ry9<{L-{-Cq<%FetS&-@x>c^m=gkpNB}q<s>f<T*Lbp&Y{Lqh0en# z(bjxC_iU!F<^ufi%MaiS`(Y6E7`!on@#!%fX*^2c9XWxyT!7xN7otHxcq0N+Ndfo* z4ZmC;!1+s`;ri{->=P<mpg-bA=ZRKK-kC$lw*ggF(9+%lQ88h5W*#FG!$kSjtdwVj ze${^gfk+U&r(cG(%8J0U47ckxHllHq7_HcAu)uC@gdB_E8c|D?w>GweCug@)q1n~m zFD`zBsfBxZ`^7i$R6_?UO*S;yo1r9=NG25|V;YP?0SsIL_L@p@toa#enua^~en%pq zKvmV;p?}QYLeI0Oaj3opMx7BRp%Ar}{VeT+(GV$X0kQ!H9lte=#zL6)nBjCUlE6q% zJ3uqqYcdo<Q#o2r09AvvqLyWEOx}by;6Xx*gHyI*PiQi#E35Fx`H;M$LQRs%ssFM$ zWA-N0IO57Cv1^k=FO!WoVKJ8x6-lDViIp`6!to#?i4fN10L+#mL}O8`k$|YZ)b5;= z8YKhfU2~|nAAmtHf$V^ve)|D#zXuM#8yoQ$7FU;Gv(}=r;xUw13ZWKkf+JSXEj~bb zStT(?1D^}jT2cvbWEGCkeZ<2m28M?*ySxC$x(^aBz!_S_`0_l8=O(bP*aAyY8TM6F zvv*g#OH8Fkj)W@yr^Ol1G(>D0yf%bKe|oS??yPI$FC5>05TCyF2`r@*xG_0~(dkhP zO<u+1{0fW<)36p6quRWHo>OO28O-OFNS?(o!jTA5M{PTI<}6<AdL1UIh}2OYN18fd zKTwZDEp@o~<9C>v@j+G+Xm5C$Wv`B$eNyGKS6WkEg|V5NSc-;GR;I@rFFcQS`%!XE zDw~|9W9v6pF5&Xn&k)FS?~|wSO4n;BHJ7uGHmBoqOQa?53Io#N^U1h;We{V<cTrnf zgSLh?xYu0xdiZ-pWf^ChI`PtTy(l4POZy^rH3*Qp!=;|Hf`0z)d3;Qwy+1RFh2@8s zAgUc6CvHBN!nfnUvj22F-imj6-rI&z0Bs1;U4kGA+d*tYc|P-prp|T{wbnWuJA8uP z8g}tnW&&(Y!+3!w)oKewfjiY-LEv}KCHkVCv>5b%>@3rjhWuvNfvh^DfCRJ~Y#U1Y z(A__{sSRkoO;;E-b^n;!RK?lqv-qr9SV1r89l=o0Ml&r_bC%wa|BwBV+vTE*gD91{ z*Ta$ik&$7WC`zOY7nxP0o5@FC<{-2CZvh4X#4hG4-o?{Y00000NkvXXu0mjfGjv9u literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_h.png new file mode 100644 index 0000000000000000000000000000000000000000..c512a6ae41488e1c47f6cef715c07fed761c5c0c GIT binary patch literal 1446 zcmV;X1zGxuP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%NJ&INRCwCFmfLR>bri?XZ)SG()^=xi z%Wk(9iYYa;U~Dx|xk(XX6EQ)J7ktwfpN)Tkuf{|a6JLxZ`ap>B1&EkPQV<nLq@`R1 ztD&Xa?oM~N-EH?aJ2StZ=gbyTwo3yKWs;Mbot-(KIp=)O`Tdl!D_2?o;1HSyYky?X zjw=L=Mx$DDG`i-*w61Hqt{+mk;94*o24P!k4kb<^na~s=0BqX^@f32)(TImo0i4?a zOj>)W;6OY@a1Pu?yu??1VK%L+pMUe&fshu=Z0jHRXW#BulTQrg0*I$@f?}k66=r%a zrGEMC7el3TscA8<m=`Wz*mULk=!U+{{S$*b_a&bUj8J4_r1qnM^J(?ScZY|H<x)s> zD^NDdzLvIVQDTzs;>d-b(HjKsn;6{w%45Na=R|6wu{sZA5cu`cuZGH%a_Hr~190Q& zZ8n$5sit8`J9cdoF7U9wZ{7BdTpQ^byE(Sud4%s77)(AIxD+Yn*|scTY$f)GpT6H} zn3d4GZ@mL8tu1U$Uu3g-MlI$`5NeFDU_(ee+u6%Xr4syp={Mh{k&8Vq^=;Q>Sr+yD zRYfa65(K4aF~!U6$kD?)XEU=KKYafK=->Pziw0U*Z}(<=)|M^&NV9B%$K?Z;q(YNF z!1in(WEcv&G<q>oE*p|n<IgeV5KrR5b`-^HES{L&`1;TR=;`id9>wnf+M*pGOR`lg z7yX%Z+R2V2Rn6=5ffmr%bRr(ax=UuoWVJpW4)UBxEqf)D4zI_DIg!XYLm0%OZJ??u zV5F*^)}dk=U|ObQblu+C2FY|XY?>y+d(5u!NgO9qOc!3POa+zQ!WT`uf(|NBam;{_ z79t1o<kTeGOH8s{F$)XD9OMe~5Ng)IFbq#Blkz%Xv%;49bU0{LA=Oc@EGw0X_+58b zR~M$%>)bnY_E+rLw6vICXq%X_)MGy$%S9v6d}}NQ;fMx8u|Sd~7@xQk4hNfylpUt9 z8uijd61>`{HEQ$w{Ma3-SUf!eM$v@f;d3yZnDJn}LN1pJ7Ye&!CY1m^qnF3;j?a1B z-kcT)X6RjvG+^6S+_GdUyLjLm)k{2ua!hon9r^gPU=-l|pXXun-)X5(ECwo;+1UL| z7kvEDC!lFTxPEI4E?>Ls8M!*rnwn3yX0jPeRoyx4?>Q{NP0O|zMb4Jxk$4ZrV7)_+ zrvc^f#^}O|A}jB``8FKbe-IixjgE5rw!8rAJ3FCwOE;W2eF`S;&X7N)Bes6LQmIH3 z7>Q89zI|v6(&Xo2<-!(d3e2GrH&skiYtsC1@Qv4COIN=mc}MmjJ6bsT^9dOK<17k= z8yjMsqr0{aj%bZqnR*q!OkgE!E=doqG)qg{%4wfn$c6|=*9+MKoIZ61{tAsjBpd~t zwJ@8Sh10{oK%rPL+N15GJNkEzHZ}wd2|?r&0~C18YyCU_ojiHs)Asg`y6HukvpfIY zZk|a_hwdfrbuJbb*5Moul#KE^`acp}cXRK+{&S7~CPR`WL53pZ*vq!6x@<*JINe)5 z665iys*uDZ);0RVj#y_l5{@lCx2bQ6)Ms<S1evosaS}=uh5&-O6q&p5jO`*EmZMlv zBP0r6)ZeU7irjA1l4S|`9LJhq)ibAQ!?4vzC!k;lsV<jI(EE|mN7ZvnECmFwThrxW zOqsnDIa}HRFzP83533FmS)jW4{hA4}%8pAcEp4ko>nw>h;PFg@UV5k$kGo}|eQ!3O z&uf0af6X^$g+jq;3~J`%+4=cHXisg2|GxkO042$Wv`i$^?*IS*07*qoM6N<$f~$$J Ao&W#< literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/shape-editor_n.png new file mode 100644 index 0000000000000000000000000000000000000000..e583f254994905b66a9ddcd090eda4fb601bc184 GIT binary patch literal 1006 zcmV<K0}=d*P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#kV!;ARCwC#mRnB~K@`W&?6loB*p^GN z0#d~Qt=u9ing|4uM0o*$SCLmVzWL&#AHhcx@Duo=F(5`s#CQn^SmT2cg$gwVp%>bw z5W3xqXPT{9F)g&BKIkMTv)h@S-=6vZ=X8mpDDbBn2Y;dchyK>4OE*w?xZ;QVrZdg^ zu)B>hhcPn!>gC&PK=X^Azh9Gd?$tt7;4StugAzhkv5#Q?bn3-(_nt#jKpoXpQXK_B z5Q4Vi;yI3Ru(q};<JkIc;96)#O;bRF!BE-O-VXWs1#JF4Nfc?@**5O-zyLj3Q&Y(E zeBY_&mg}cmS{r{!Q?*^g1uHN0_d{7(DHjf}6Tg4Wyt%na@E(e7HaO9EjEik-DEIC^ zD4d;{?!#x^VXz%gvUV?KdBbQlb`D;-0!=566Z`&RQdd_`aL6DgL`Y!d<>diy<e{jj zko5KSaI7_-ZLe38A-Nox&Ys?0sIRLbyn*LTCKGWu4zh*f3Mh7;&xcYHVA`^<n$1vT zvoViUs8BAabBdWT2$x(P-@LTs0N{2fhkU+eW-81$kqiN&qT&cBvQn+C%caXUBQ&|7 zkRaF-NtBXXirKIsy=P$o<`+C<Jsc+CNCbkx5R{aZppc3cw>Uo&G?S5H9*@WDayfxF z7?MAXJ{*I9-%n*(J{<}L&BH@OO9cgmtK|*{*zI<ZWeF&yY^oC@lnZn|;14u<y<cE! zYYUz{egZ2i%T@$dp}|mQ{y-En3<Ug<U@-6z=X>>fJ?jy!9Kvo=g_7+~&Uj>Eu^5bx zkArt<iQ*PJ&?Kc*)lRr}br38T3%vg@2X439iqRAb2AfdhEtKDjkAA|qt8THB)j9pT zvI3GUSrF#L0>O7*?12mCJHTW#0`pewQ5TdSECZLz1tTM)u(bHuh>sa5)jh<Xs^?YO z4$io}q9pcWf5>9BLRVKe)YiC|J3+?M$x>h?l<E9zVuCq2s?+I*1tC84`sIr-Gn)#{ zN>f(cnKr<*do(r%vokY*br%{M>ap(nQRl}HjYguF*&R_7hY=Rl=$Fd!r^>Wb!Kba~ zI-EGCT9V`zMUiXKNac86n|x!cyBincH{VQ6reF+|P(#2FQzbE*e;K9=uLf0}$Z1?j zG%!&GrJz65UoCW}g3lg|s|%*84Aoq7>cU!J#$vx4sG@NnNfR0M%LTgq1@`14nfx;! c+usBj05`CYY+0CP3;+NC07*qoM6N<$f)E1EWdHyG literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_d.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee2faf4711837d1596dd48ff55b4f9cdd9938fe GIT binary patch literal 1020 zcmV<Y0|WetP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#o=HSORCwCFmdkGwcM!+F-j`!z$F<|c zHW0KBLY&Y*N`aF0&?6OxO6F2kRUaHU^i=p0dMv6cQB_f|72*O13l6PRPy=caX_^Pb zHWZx3CiW&y>}=vV@$UMuW_HseRZ4;ra7G%f_o4atJHPqOZ!7}?1APF(<RThi7K;Te z%Yx%L2)vNw1wO<XK$}$%J30=+?RJA<7&OXcc|pv{i(vz50^jWIg|oTY{6~$lN~MC^ zw{D51EJI<Vfrc6Jd7g&}HB#DW;D07qtzfXj#*?@$K_i)AEXfNm?p0MmWMbmQ{f%%K zh2{L_)l6Bf8#<Yb;d<;RtQHmMdewY3H=BW!PMh%g%q%8iF~p+Lm&U3adN=+HCgeCK z<p@?+3g)v!G69bEVCva4#>U3s*tH8|;}2dRbSOQ9vZ~|0^anwUkW$mARdwtWMMyIe z9!8@0;Phz}R#x#Wk$83J<J34(c^Oqr#~tZDGWj%;i6lH;FIwAtn4g;`m3DLmg9sk# z#Gk{%uMWKzzk`ZaLxq%zf)7*Kr&!A8;qkPX@3Xla4)=6J)im_?pTd)PeA9xe3wrj* zr?~dfb(9nxUmQP={f=(fY&I}d{!{-<QX#20(Afn=QQ&g7AmH<1baZt4P@_^cQs8vC zP*#lJWh`WKXr;jAl7?s`g1vk85>Pe0NZ_~n`Y`_B;r5}0{4xYVFx!`r-gHWWl~mg8 zb}Y&=rX>k&fq<z}A;pI3&aNO(tKspZN9(u4deU1el}KH7O{uD3nbxyJ>UCWQN1HuL z4Z`bfF^@2#;}~naQY>!VgI@-8(P3b-+X!4ng`j?_)*~H9O{k`o;BEEN7FxyB)gR$J z^*%UXJ5HQ9jy=0~qs<@K9&|Rd)(8TAC$zkPxxb^x7Rq253wUxTxtm=6bp#IbmScW+ z-tNU4AASsM7y8W)cQG?3qekgchfd=6dsh%)s&LRmV7L=>C2#)sdu=p>!bk>9Y7uUi z2TXg<)}TKRPT}%M5=}}GL8}Gde|HX{(B*Z%y}ADoSdRejCuiV4`#GA#!&_g|`CJhf zuKYy@AP==Fz~O2lVBdy;y1)Jsou6H-?=au!#igqeba<RN-+ut_9`)nBlkJ-ZsuyTN zS$6%c`t4WmH0&T}ICf{ol>b3#4&8aERj?)(mln<Ou+cU<Gh<wm94`o={Bj{YIQW~$ q^8yts50+z%X{Hg}Bzfq$00RJbV{^G*+*O1C0000<MNUMnLSTZu9pC2w literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_h.png new file mode 100644 index 0000000000000000000000000000000000000000..68c21d9e4d2092e18f6ae5724cd838fa6dfb2da1 GIT binary patch literal 1196 zcmV;d1XKHoP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$PDw;TRCwCFmP=0?R}{yO=V8VV?7`UB zgy0S~t&;Qs>7pcx0_saF+NfK0U1pcBkdM&M(5|wrL>KL*sw!1YDUUe3+5iR{?6CoR z{1|&Yo;%ZX$2fqoS=C(WFMDRL?oa1`&OP@!)>c;|0I+}(q5nfcz2GP?91e@YaJcWp zlUz>B<#G$0X_?~C@Gw}!=sT2hvT|1BEDL}T0@e+9YoaI*p%$1X0dO$=hgv339%Gp% zn1q7Oo?6-cGnUWg1MO#3Rk`KmmC5$=;^N|bu}~1MTf;o#P#)8ILBKGKuG&f^A}eo} zzEIVwzoF|4eRr5n2aogl+Z3Kor$t$oAG}`QxYKKmwkU`47-R#%KfRRLTbU}93!?|8 zd*Mo{?4|G0=|hQO8Q4!IBU_uB(;l}6(y7$Ub?dZ6d96rkA@$HvK6A&=4cN{7J6Wr# z{?z_K5Yr<_k_2)te><C%XXfT+!5}1(+}({>*Q`Z3X3JiT6ho&K+^LP^s|Z>sHXCSZ zE*;O8Wbx?eC>9KbU^o~8wOW-12M2{%JPwob7;JC<Gt+IIx3Vp^3+c?>E|XsGChRDi zC>Q4{W>1d7b*&x}heWJG6Y82a7N3lR)@Z=g)D#qoh3QhMEcAp@dnwekh+R?Ls_nCn z=GUKo{p<%_)8XL<kN+C;OS;?b<{b`(IZUNRLJaSC^c|?z>);oMU?emGo0#KX)oXK* zXGdy~s}l@^Fj%dgL7xwrngM!4XG^7$KZv%XP0nO;Z;V3fb<I`{!T0Xnr5$Ic7nE}B zNQp@!y=nz+5ucOiu}B#NHsSQ77#a`+R&cqXQYm{+3x%MFZH-2WHiL$eCSu^^o#LCF z9jTkWHV4I-XE}8^E_V<5uGwrt{`eT02-Pr}O1TUT%pclhF%HJq+u7knzaKb`vsF|S zP;08)>uC4s9JB>e9!Cp;mU1Um5CjDms^O+~QYgo<JEONoPjy{qo4U?V#3nY-a&K<+ zkI%j1v2A{Es3eMhIUbM6fj~eZ<SLX>DUYKbQ#y3C{F8zz(BELdQ*CMvNNsO<g_2t3 z&~7KkT0F~WE|ito6O7}7X^+n*yF!?O_dixlfA6E2^0;;z>>NM+l9;QMagSootYQof zGJnsX@Cg{;Sa9)F9Bv~P+&pK`*K4(inmLTHCJ`8y_cv}Ns&sAo$fM^e`hPjfrB@j# zb6j~qFn^3ilrQH$`F?ix+x>2f{QlkZ=Pb)=zUaiuk^2wV21X^Nb)r~3PnuRCk@HAP z6<t_b&wj)3yuGiSEU!I!^xpo(ITy#R5AXjdefnhWZ2|U}cOI$LK2Oh+{_ApPPH;Pw zXvF(6I_lfM`)*La_`P#P=e}CI?)%kRqENGSu(rpO$KyYDPWnHG)ynDRNc(?#IE(Zq zp?0L!4M%F{lwN?V)vD<6c=`^+v)R6I)11Cg6lDQ*V>kT&1Q-A<jsNS#7?leE0000< KMNUMnLSTYS?nZ<F literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_n.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/transform-objects_n.png new file mode 100644 index 0000000000000000000000000000000000000000..51f81427f3035845d514c10b5f11cc863a2000a9 GIT binary patch literal 1058 zcmV+-1l{|IP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU##7RU!RCwC#R$FLPM;M+t*F9&Wdm7ko z*1B;MO^~ihBUY#u=>{UcCKfaVt4N7b&8?}aQV^*RqCQkD7NOCI7)4R=RiV%NP^gg0 zgEW$jtJc)u-g2_%?78)yWmXPxSGFtG2RraHGiPT0Z@z!N`Ojd-anOHV81xRx|DbQR zaEm{z3fH|dY4FewZT0naGd+8{|0Ml^?+<yPik?1s{5I{Mg6c=;CrbfM#iq+=+WXy5 zi)C5Ip&2@Q;v^3>23rmGS>J&nsekAo+2sJ-f)45rzj1gC!`P+k`nqS&X4mo@AA(OT zSS!bK2xDd~#(1~aE0d}HU;>REej`xRG}^&XB9Yhv-TI!v0}+qMqck@)5yu;VVP9+X z3nU1_C;J8lKc&D-#h|;o_p|`hFc_Mj&umszwH}<e)z*e^uqJ>d$rG)vuJ+Z2mZQeT z2E_CHehSJ~3~Cr=I~X!@2E+?btLl6+&vTIv6os*En=oD9(6AC|x{g|!n;_h{vtwIl zH3elV0^PWA!}YBl?QOYS4y_1>M}ooNf=^Loa4tNGKL!EeKusWkve_(>Wf=va9jXm= z!BV~=(6he|6G8av>Q*2E3g^menzrmizaPOlE{VtE>jQZ}EpH0|ye9H75mz?_Eh(ti zF%9|seopd8c~DK$8)VtbiK6HLvp_nXY4rJ)=2>Pn4aPO}(Xt?_s`7?FP@5<yQ&w_t zmt-P|#>OWAR42}zR4SEkL$QIa9*f09MNtsPad~_3B7szOfkc9(Y;#~%*`Sbprhhv7 z%Un8>@e%d@Jv;lG?3HJd$s}1rGSaPNGF1a%J}gP{9RPi7S>|Lqm7csjaz0MQ#L^Hu zWr2S6b+>Z=kNX%L{PlUX6-|tf`JX<0GRN}*g2Z5of{@?^A;qzrPuKKqf+$YHby_0H z@(j7&l%eyl!ppMk5lDaG?K^hn&vfqj^AiiIiUiR_qAijugfPR9d4(krj8TN+_^NOE zzdd&D%&CXu))fOT3c9zqFKU_=<Wn2595=(-_T~qZ6L)D?9hWowWoK6g>J->8B#-N( zBNt+>@$#5Lf>(gyMe>RvptkL#ZeAVz9fm}AAYRI!W%gaYbn%)i{S}fvE_G#I0c9BG z2&gZ{F!*O!bMNwn;VH_HoRdyjyev1ajE=ZAf$6wG=i9|V@#2RW{hwmGbBRjdq4Liz z26Ztn5Ga5-rT(X-gj5KP<x-+atyr<?rckMKEJwYLD@0DQofny3`E$kP#CltAB>dhy cw*L`e01D!UVcy`$8UO$Q07*qoM6N<$f;A=f8~^|S literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world.png new file mode 100644 index 0000000000000000000000000000000000000000..e08a1b89d40951779331f50fc3617f7951fad271 GIT binary patch literal 807 zcmV+?1K9kDP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!$w@>(RCwBA{Qv(y1HAzg!vKI$OGyTG z4b2V%;nV%ZDr6zW)G5<uf_&ru>eZ`8Al(ndg-s2$AE43)kbEtGMIH_qNy#!3CQaSX z$jIpM@83Vi_wV1w1BDcT803I_AO>0V0Eia?aXp9)v@j8yIYe99(>D?1%l*8(ybPZ| ze}3@)|3AaepT8uqv$OS^n3ymqDk?DCzyE;Y%$YL`KY#uL`EUZoar*b~pL%g|F@})f z00w4eW(A;cLB7fN_4Q$JcXeh^Q&9%fAU@RRlqIB=w$6axzyCN`T39f!v$Hd(si`sm z`2}A-e^$`b(MC}V<tqT=H35kCgJ`l6(%(OS`q_Y%@$vC8EL^mN;mzweU~vvk4u&sZ zzA*6c@W9oA_(%!s0Th!Kr!{pA3BbfNpPh}J;peZP3?SOY)dd`X*REX!`dW=4HaZfn ze$|?F3|FpPJphbTM_gtSw6wCi_P(~ZjzWN+H^YimYZ+Xe9T^nl<-l@#_8nl@yLT@G zKR-W%p`jtety{MlUcY{Q0Vs6=S9T#7rzNEo34i|lQE&xX1Pa2Gq<FZ6AmHNUz#t(Z z$?)>UONO&&&mI6;7znh;5LQ?~^D8C*`4kr}C@i@@d+z-IzkdJzj|)6|{^CDS-T&OY zg8#^RYwMd5aG8tKr@48B1t4EqTUjyu`2K_8_n$vl)k#W-Ggw+%F@RzzJ2&3|CJyxR z0+O;!dS<r3@87@c^$iRdxVShOzJLFY*W$3?Kn6h}Az&*1JD=<V|KH!g1<cG$0@~VI z3_pJSWcdE$2SJNtfGHSQfEc8vWfTyy6eIP<CnN~~v&elpc?AI-T^$Bi7FGr}HdY2! zRuIhwrde587#M*N9qie=kKyKx>mPt-8ZKNg?*Y-Z%)h_?KqZL4{kwM=K$r{wt6>7k l&Znczl~GFvt)&0~1^}3&a0su@aK!)s002ovPDHLkV1gS{Z|492 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_d.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_d.png new file mode 100644 index 0000000000000000000000000000000000000000..da4cb58569b2ce5f7a3eb6770b50f801dee82039 GIT binary patch literal 983 zcmV;|11S87P)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#c}YY;RCwCFmU~E3Q5?s=+ugl4TRNuM ztfj3@GgGOo$jZcoUi8!~&?3Sj0?QzJ=RXlhQU)bRYK5t+@R1SG%ghR~$1<}rU0G;m zt4+80s4X+=-V;jVY))EVIGlTa_uTWjzjJ<v-%pU9o<0pgG1nAuf&>BqD2jqeBmzS- z4KzbXiFlw3%P3STUr41=2!%p8OGaa>qQz(`=Ai@_6dVj!H#hP}&Qf=GH*S}dD2Q9U zht3A_;@78X8U)I3sk4E-Vptac^Z;95me`#`bI$!!s5Wn>{b=TP)nRCjUW1(Klla!I zM_tYi()UU%5A1uk^}g?H9fcOw<f6J=i>V&7!E)oe^9dNgU@JaVsL6XC<QcLOAIdVp zO1!|jjTx|pL!Lf+Ux6w^9b^ItGM=VlY|LRu{lie7zaK6#6@fDn85#5T$s5ybuHz82 z@I@X1#lA=iOM%()6&IhJfnHMx#fk$^u1bPO&_dGlW8OZPh77&;>KZEbT0{rLLCm;e z{+MuFetQ#*KRY3t71M7nKkn*iMf3f0s7_l;`eB{vIZYPCJv2s(y>RGCBHna-B=!>N zC@klK<<GP04IYW#JdAMn2J7MDxTjYhsnOhSLQhu@Hs^1FuFZfQAzKmYH3#)Q6tYWG z`^|rM`3Q;RQDgndmh-*Co?fiHfQWISNLaKR$F3emT<9hQO`L%hh=upv)5xk+W5UVh zn4}1W`E@z@W~9uIcs#7=y;PoyrZzJ+%#DZCbu^M=(+IFv>m#-yNTS4rFGj%d9P)@Q z2-$Pv_npqzX!D-hg{mK@IWK2H?x`Gf&aR~g@VZNY(vJGRpyIEKZ1y_GJw1E%6dFxt zM9++c)ndhWE;ZSbQtpq~;AO}(u&Ang*k_lA$2)dq48~8$t0=%~RV3V9y<o9e{t8Z9 za}X-ACsK-%?Z(KU(41?T_-_4y@PH+-TED?!wb}*mjoOde&pKojo%nlRwPn<|=ur6h zI{YL)sBfx8qwWp3+J+=xnii49=U<^vFyPxc@+ru^SAYf67h{q#aOlwdTRAXyTA=CF zLes1rR+>AmK8nnwT-#8J8o0yT<Mxr}ED1%_|0z@YLHV=#?@*Ru8%$;sDG!}3-A9rn z8bmb1M76bd6ql9VQP4EQRpB%!5ye+C&fqq1E$Wv50{{YgG_ozt$*}+c002ovPDHLk FV1g7g*E#?I literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_h.png b/Templates/BaseGame/game/tools/worldEditor/images/toolbar/world_h.png new file mode 100644 index 0000000000000000000000000000000000000000..9f68f1bc0ca3f6a3d17966ad329ddb55e0187d03 GIT binary patch literal 1185 zcmV;S1YY}zP)<h;3K|Lk000e1NJLTq0012T000{Z1^@s6fXajt0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$LrFwIRCwCFmi<p#RT#&g)(c!{=x*Dw zmbY;gwS`Trn~v(p%H~WL=WIe0(W#C8<}dRX6aNLH(O;IBV4^WuoMS4PBe?OhL{wPE zONw?|p#)Y~woyyVr9gWJ_r>$vQtY}yjifxux2N~qbDz(7dCqf=YSiN~0YD281GzVj z=p2PWqtVFhG#YbGoC^k-U@+K1NwUP~3<i)zA?Hx!+?&0}P_hgliXzC{Ft-vB^3*c< zl_U`)EVm&4P#2rE9pNBPElUzeB34LSlK1xxSJrlfgFJPjLIgn&vdS|x$hNz`b42Iv z(tT$?bz+%(S6IvhlK(TkM=3K(coboi9*F;GO+1r>_GmoH`t{S}_<ZeR>l^G-2m5Ax zgY_!%+<*QHXW0|hKLg%=dp@n<=wbt(?i+a&l0_m_7T{lj5jL_g&uWXbaMkylv-#M& zt+%iKV)YO8x*mV}dDykv0)Nkpk+to$8mr7*dyl;3M&AztAu&=(l0hUxSE)9~j`#=d zrA#Tj+VDDj-T5WcGu&y{P5Uj+zSIoW&onA+Dr=qtW>+N)_nfb<5k+^pk3`Tafg~#< z#ci{Z25$6OA`20=q3$pg78HTGq86rS1Gdl#&m@6K!)IajJ~Q5b!P?*UnG4HxUxb4^ zk{m=OqBh&7BjZDkM}PuGql2&7PeW)vq|^uL0*FV$P^dMmx5Z~PLse~nF4l1xeJPhZ z&B#+mDu^40dC$-#Yb+LHgjE4PKl2GhxCp%b!U?FUs)c0|2|sp!|3C+ayUqfw*%2r% zsp3p#n^GoyAx9ahh#Qt@qxiis>9yCed*C(4n{cM%8*m&v4raCn4wH8szqUj7z-55b zA4Bbvd*SYs5BS*PY(bHhi$&+?0*#JO_lG>Sw6><1M%m@=w8r8w=2*jV&}sE>>f}cX zFli?mo1koGIgExw5WM9LXibl`?Q?$EsziJDx32hNm`?SV;9wxhlCn?5waQdopCF?o z(`YKG*l@j1zT$$YAj0)<h%Mc7Xbiuzyy*JbP+`7C74G0u{UHZ=%9<iw!gWp2g)2YU zVzD^Wc;G1TaUNEM%%{-qG8PW)v%^?a0I%O;N`7BncXTw}CvuRto(5ADUI@{?$(#25 zmi?gB=pi1DZwh{=>3z_X>F*ina;E!wSeScpdecsWOMRU-QcYVe2Y~1AL!9Tcf?ZAT z!Q$eQ>8I{C+vcYY=3Y5*!Xz!ih3U!Z$@=n=G6>wBf;+P_ke{Cqd3kw6lu}X@1#0!f z8LYap8g5Sb>~-deu}3SaxlElyULyCVB<0>eE?ZYtR_I%I0)YA-Mt5Gh==k96Q*9X! za^;kf5@%e}njFoTGu>OgCCF*X{@;2f4JPJ#y9`+r$P!2!^eQJNuN`gM2}K^>d%2~h zB}Svs<Q%xXysTWfF?!E&^Ybl4+jhhMM}PqU<A>6%n8U4500000NkvXXu0mjfqY6Nw literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/worldEditor/main.cs b/Templates/BaseGame/game/tools/worldEditor/main.cs new file mode 100644 index 000000000..82b83701a --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/main.cs @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function initializeWorldEditor() +{ + echo(" % - Initializing World Editor"); + + // Load GUI + exec("./gui/profiles.ed.cs"); + exec("./scripts/cursors.ed.cs"); + + exec("./gui/guiCreateNewTerrainGui.gui" ); + exec("./gui/GenericPromptDialog.ed.gui" ); + exec("./gui/guiTerrainImportGui.gui" ); + exec("./gui/guiTerrainExportGui.gui" ); + exec("./gui/EditorGui.ed.gui"); + exec("./gui/objectBuilderGui.ed.gui"); + exec("./gui/TerrainEditorVSettingsGui.ed.gui"); + exec("./gui/EditorChooseLevelGui.ed.gui"); + exec("./gui/VisibilityLayerWindow.ed.gui"); + exec("./gui/ManageBookmarksWindow.ed.gui"); + exec("./gui/ManageSFXParametersWindow.ed.gui" ); + exec("./gui/TimeAdjustGui.ed.gui"); + exec("./gui/AddFMODProjectDlg.ed.gui"); + exec("./gui/SelectObjectsWindow.ed.gui"); + exec("./gui/ProceduralTerrainPainterGui.gui" ); + + // Load Scripts. + exec("./scripts/menus.ed.cs"); + exec("./scripts/menuHandlers.ed.cs"); + exec("./scripts/editor.ed.cs"); + exec("./scripts/editor.bind.ed.cs"); + exec("./scripts/undoManager.ed.cs"); + exec("./scripts/lighting.ed.cs"); + exec("./scripts/EditorGui.ed.cs"); + exec("./scripts/editorPrefs.ed.cs"); + exec("./scripts/editorRender.ed.cs"); + exec("./scripts/editorPlugin.ed.cs"); + exec("./scripts/EditorChooseLevelGui.ed.cs"); + exec("./scripts/visibilityLayer.ed.cs"); + exec("./scripts/cameraBookmarks.ed.cs"); + exec("./scripts/ManageSFXParametersWindow.ed.cs"); + exec("./scripts/AddFMODProjectDlg.ed.cs"); + exec("./scripts/SelectObjectsWindow.ed.cs"); + exec("./scripts/cameraCommands.ed.cs"); + + // Load Custom Editors + loadDirectory(expandFilename("./scripts/editors")); + loadDirectory(expandFilename("./scripts/interfaces")); + + // Create the default editor plugins before calling buildMenus. + + new ScriptObject( WorldEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = EWorldEditor; + }; + + // aka. The ObjectEditor. + new ScriptObject( WorldEditorInspectorPlugin ) + { + superClass = "WorldEditorPlugin"; + editorGui = EWorldEditor; + }; + + new ScriptObject( TerrainEditorPlugin ) + { + superClass = "EditorPlugin"; + editorGui = ETerrainEditor; + }; + + new ScriptObject( TerrainPainterPlugin ) + { + superClass = "EditorPlugin"; + editorGui = ETerrainEditor; + }; + + new ScriptObject( MaterialEditorPlugin ) + { + superClass = "WorldEditorPlugin"; + editorGui = EWorldEditor; + }; + + // Expose stock visibility/debug options. + EVisibility.addOption( "Render: Zones", "$Zone::isRenderable", "" ); + EVisibility.addOption( "Render: Portals", "$Portal::isRenderable", "" ); + EVisibility.addOption( "Render: Occlusion Volumes", "$OcclusionVolume::isRenderable", "" ); + EVisibility.addOption( "Render: Triggers", "$Trigger::renderTriggers", "" ); + EVisibility.addOption( "Render: PhysicalZones", "$PhysicalZone::renderZones", "" ); + EVisibility.addOption( "Render: Sound Emitters", "$SFXEmitter::renderEmitters", "" ); + EVisibility.addOption( "Render: Mission Area", "EWorldEditor.renderMissionArea", "" ); + EVisibility.addOption( "Render: Sound Spaces", "$SFXSpace::isRenderable", "" ); + EVisibility.addOption( "Wireframe Mode", "$gfx::wireframe", "" ); + EVisibility.addOption( "Debug Render: Player Collision", "$Player::renderCollision", "" ); + EVisibility.addOption( "Debug Render: Terrain", "TerrainBlock::debugRender", "" ); + EVisibility.addOption( "Debug Render: Decals", "$Decals::debugRender", "" ); + EVisibility.addOption( "Debug Render: Light Frustums", "$Light::renderLightFrustums", "" ); + EVisibility.addOption( "Debug Render: Bounding Boxes", "$Scene::renderBoundingBoxes", "" ); + EVisibility.addOption( "Debug Render: Physics World", "$PhysicsWorld::render", "togglePhysicsDebugViz" ); + EVisibility.addOption( "AL: Disable Shadows", "$Shadows::disable", "" ); + EVisibility.addOption( "AL: Light Color Viz", "$AL_LightColorVisualizeVar", "toggleLightColorViz" ); + EVisibility.addOption( "AL: Light Specular Viz", "$AL_LightSpecularVisualizeVar", "toggleLightSpecularViz" ); + EVisibility.addOption( "AL: Normals Viz", "$AL_NormalsVisualizeVar", "toggleNormalsViz" ); + EVisibility.addOption( "AL: Depth Viz", "$AL_DepthVisualizeVar", "toggleDepthViz" ); + EVisibility.addOption( "AL: Color Buffer", "$AL_ColorBufferShaderVar", "toggleColorBufferViz" ); + EVisibility.addOption( "AL: Spec Map", "$AL_SpecMapShaderVar", "toggleSpecMapViz"); + EVisibility.addOption( "AL: Backbuffer", "$AL_BackbufferVisualizeVar", "toggleBackbufferViz" ); + EVisibility.addOption( "AL: Glow Buffer", "$AL_GlowVisualizeVar", "toggleGlowViz" ); + EVisibility.addOption( "AL: PSSM Cascade Viz", "$AL::PSSMDebugRender", "" ); + EVisibility.addOption( "Frustum Lock", "$Scene::lockCull", "" ); + EVisibility.addOption( "Disable Zone Culling", "$Scene::disableZoneCulling", "" ); + EVisibility.addOption( "Disable Terrain Occlusion", "$Scene::disableTerrainOcclusion", "" ); +} + +function destroyWorldEditor() +{ +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/AddFMODProjectDlg.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/AddFMODProjectDlg.ed.cs new file mode 100644 index 000000000..a005ab4b3 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/AddFMODProjectDlg.ed.cs @@ -0,0 +1,253 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + + +//============================================================================= +// AddFMODProjectDlg. +//============================================================================= + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::show( %this ) +{ + if( $platform $= "macos" ) + { + %fmodex = "libfmodex.dylib"; + %fmodevent = "libfmodevent.dylib"; + } + else + { + %fmodex = "fmodex.dll"; + %fmodevent = "fmod_event.dll"; + } + + // Make sure we have FMOD running. + + if( getField( sfxGetDeviceInfo(), $SFX::DEVICE_INFO_PROVIDER ) !$= "FMOD" ) + { + MessageBoxOK( "Error", + "You do not currently have FMOD selected as your sound system." NL + "" NL + "To install FMOD, place the FMOD DLLs (" @ %fmodex @ " and " @ %fmodevent @ ")" SPC + "in your game/ folder alongside your game executable" SPC + "and restart Torque." NL + "" NL + "To select FMOD as your sound system, choose it as the sound provider in" SPC + "the audio tab of the Game Options dialog." + ); + + return; + } + + // Make sure we have the FMOD Event DLL loaded. + + %deviceCaps = getField( sfxGetDeviceInfo(), $SFX::DEVICE_INFO_CAPS ); + if( !( %deviceCaps & $SFX::DEVICE_CAPS_FMODDESIGNER ) ) + { + MessageBoxOK( "Error", + "You do not have the requisite FMOD Event DLL in place." NL + "" NL + "Please copy " @ %fmodevent @ " into your game/ folder and restart Torque." + ); + return; + } + + // Show it. + + Canvas.pushDialog( %this, 0, true ); +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onWake( %this ) +{ + %this.persistenceMgr = new PersistenceManager(); +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onSleep( %this ) +{ + %this.persistenceMgr.delete(); +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onCancel( %this ) +{ + Canvas.popDialog( %this ); +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onOK( %this ) +{ + %objName = %this-->projectNameField.getText(); + %fileName = %this-->fileNameField.getText(); + %mediaPath = %this-->mediaPathField.getText(); + + // Make sure the object name is valid. + if( !Editor::validateObjectName( %objName, true )) + return; + + // Make sure the .fev file exists. + + if( %fileName $= "" ) + { + MessageBoxOK( "Error", + "Please enter a project file name." + ); + return; + } + if( !isFile( %fileName ) ) + { + MessageBoxOK( "Error", + "'" @ %fileName @ "' is not a valid file." + ); + return; + } + + // Make sure the media path exists. + + if( !isDirectory( %mediaPath ) ) + { + MessageBoxOK( "Error", + "'" @ %mediaPath @ "' is not a valid directory." + ); + return; + } + + // If an event script exists from a previous instantiation, + // delete it first. + + %eventFileName = %fileName @ ".cs"; + if( isFile( %eventFileName ) ) + fileDelete( %eventFileName ); + + // Create the FMOD project object. + + pushInstantGroup(); + eval( "new SFXFMODProject( " @ %objName @ ") {" NL + "fileName = \"" @ %fileName @ "\";" NL + "mediaPath = \"" @ %mediaPath @ "\";" NL + "};" ); + popInstantGroup(); + + if( !isObject( %objName ) ) + { + MessageBoxOK( "Error", + "Failed to create the object. Please take a look at the log for details." + ); + return; + } + else + { + // Save the object. + + %objName.setFileName( "scripts/client/audioData.cs" ); + %this.persistenceMgr.setDirty( %objName ); + %this.persistenceMgr.saveDirty(); + } + + Canvas.popDialog( %this ); + + // Trigger a reinit on the datablock editor, just in case. + + if( isObject( DatablockEditorPlugin ) ) + DatablockEditorPlugin.populateTrees(); +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onSelectFile( %this ) +{ + if( $pref::WorldEditor::AddFMODProjectDlg::lastPath $= "" ) + $pref::WorldEditor::AddFMODProjectDlg::lastPath = getMainDotCsDir(); + + %dlg = new OpenFileDialog() + { + Title = "Select Compiled FMOD Designer Event File..."; + Filters = "Compiled Event Files (*.fev)|*.fev|All Files (*.*)|*.*|"; + DefaultPath = $pref::WorldEditor::AddFMODProjectDlg::lastPath; + DefaultFile = fileName( %this-->fileNameField.getText() ); + MustExit = true; + ChangePath = false; + }; + + %ret = %dlg.execute(); + if( %ret ) + { + %file = %dlg.fileName; + $pref::WorldEditor::AddFMODProjectDlg::lastPath = filePath( %file ); + } + + %dlg.delete(); + + if( !%ret ) + return; + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %this-->fileNameField.setText( %file ); + + if( %this-->projectNameField.getText() $= "" ) + { + %projectName = "fmod" @ fileBase( %file ); + if( isValidObjectName( %projectName ) ) + %this-->projectNameField.setText( %projectName ); + } +} + +//----------------------------------------------------------------------------- + +function AddFMODProjectDlg::onSelectMediaPath( %this ) +{ + %defaultPath = %this-->mediaPathField.getText(); + if( %defaultPath $= "" ) + { + %defaultPath = filePath( %this-->fileNameField.getText() ); + if( %defaultPath $= "" ) + %defaultPath = getMainDotCsDir(); + else + %defaultPath = makeFullPath( %defaultPath ); + } + + %dlg = new OpenFolderDialog() + { + Title = "Select Media Path..."; + DefaultPath = %defaultPath; + MustExit = true; + ChangePath = false; + }; + + %ret = %dlg.execute(); + if( %ret ) + %file = %dlg.fileName; + + %dlg.delete(); + + if( !%ret ) + return; + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %this-->mediaPathField.setText( %file ); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorChooseLevelGui.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorChooseLevelGui.ed.cs new file mode 100644 index 000000000..01c6d9fbe --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorChooseLevelGui.ed.cs @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EditorChooseLevelGui::onWake() +{ + // first check if we have a level file to load, then we'll bypass this + if ($levelToLoad !$= "") + { + // First try using the file path raw... it may already be good. + %file = findFirstFile( $levelToLoad ); + if ( %file $= "" ) + { + %levelFile = "levels/"; + %ext = getSubStr($levelToLoad, strlen($levelToLoad) - 3, 3); + if(%ext !$= "mis") + %levelFile = %levelFile @ $levelToLoad @ ".mis"; + else + %levelFile = %levelFile @ $levelToLoad; + + // let's make sure the file exists + %file = findFirstFile(%levelFile); + } + + // Clear out the $levelToLoad so we don't attempt to load the level again + // later on. + $levelToLoad = ""; + + if(%file !$= "") + { + WE_EditLevel(%file); + return; + } + } + + //If no valid name, then push the level chooser + Canvas.pushDialog(EditorChooseLevelContainer); +} + +function EditorChooseLevelContainer::onWake(%this) +{ + // Build the text lists + WE_LevelList.clear(); + WE_TemplateList.clear(); + + %leveltext = "<linkcolor:0000FF><linkcolorhl:FF0000>"; + %templatetext = "<linkcolor:0000FF><linkcolorhl:FF0000>"; + for(%file = findFirstFile($Server::MissionFileSpec); %file !$= ""; %file = findNextFile($Server::MissionFileSpec)) + { + %name = getLevelDisplayName(%file); + %n = strlwr(%name); + if(strstr(%n, "template") == -1) + { + %leveltext = %leveltext @ "<a:gamelink:" @ %file @ ">" @ %name @ "</a><br>"; + } + else + { + %templatetext = %templatetext @ "<a:gamelink:" @ %file @ ">" @ %name @ "</a><br>"; + } + } + + WE_LevelList.setText(%leveltext); + WE_LevelList.forceReflow(); + WE_LevelList.scrollToTop(); + + WE_TemplateList.setText(%templatetext); + WE_TemplateList.forceReflow(); + WE_TemplateList.scrollToTop(); +} + +function WE_EditLevel(%levelFile) +{ + EditorOpenMission( %levelFile ); +} + +function WE_ReturnToMainMenu() +{ + loadMainMenu(); +} + +function WE_LevelList::onURL(%this, %url) +{ + // Remove 'gamelink:' from front + %levelFile = getSubStr(%url, 9, 1024); + WE_EditLevel(%levelFile); +} + +function WE_TemplateList::onURL(%this, %url) +{ + // Remove 'gamelink:' from front + %levelFile = getSubStr(%url, 9, 1024); + WE_EditLevel(%levelFile); + EditorGui.saveAs = true; +} + +function getLevelDisplayName( %levelFile ) +{ + %file = new FileObject(); + + %MissionInfoObject = ""; + + if ( %file.openForRead( %levelFile ) ) { + %inInfoBlock = false; + + while ( !%file.isEOF() ) { + %line = %file.readLine(); + %line = trim( %line ); + + if( %line $= "new ScriptObject(MissionInfo) {" ) + %inInfoBlock = true; + else if( %line $= "new LevelInfo(theLevelInfo) {" ) + %inInfoBlock = true; + else if( %inInfoBlock && %line $= "};" ) { + %inInfoBlock = false; + %MissionInfoObject = %MissionInfoObject @ %line; + break; + } + + if( %inInfoBlock ) + %MissionInfoObject = %MissionInfoObject @ %line @ " "; + } + + %file.close(); + } + %MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject; + eval( %MissionInfoObject ); + + %file.delete(); + if( %MissionInfoObject.levelName !$= "" ) + %name = %MissionInfoObject.levelName; + else + %name = fileBase(%levelFile); + + %MissionInfoObject.delete(); + + return %name; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs new file mode 100644 index 000000000..31f794d17 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/EditorGui.ed.cs @@ -0,0 +1,2825 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EditorGui::init(%this) +{ + EWorldEditor.isDirty = false; + ETerrainEditor.isDirty = false; + ETerrainEditor.isMissionDirty = false; + + if( %this.isInitialized ) + return; + + %this.readWorldEditorSettings(); + + $SelectedOperation = -1; + $NextOperationId = 1; + $HeightfieldDirtyRow = -1; + + if( !isObject( %this-->ToolsPaletteWindow ) ) + { + // Load Creator/Inspector GUI + exec("~/worldEditor/gui/ToolsPaletteGroups/init.cs"); + exec("~/worldEditor/gui/ToolsPaletteWindow.ed.gui"); + + if( isObject( EWToolsPaletteWindow ) ) + { + %this.add( EWToolsPaletteWindow ); + EWToolsPaletteWindow.init(); + EWToolsPaletteWindow.setVisible( false ); + } + } + + if( !isObject( %this-->TreeWindow ) ) + { + // Load Creator/Inspector GUI + exec("~/worldEditor/gui/WorldEditorTreeWindow.ed.gui"); + if( isObject( EWTreeWindow ) ) + { + %this.add( EWTreeWindow ); + EWTreeWindow-->EditorTree.selectPage( 0 ); + EWTreeWindow.setVisible( false ); + } + } + + if( !isObject( %this-->InspectorWindow ) ) + { + // Load Creator/Inspector GUI + exec("~/worldEditor/gui/WorldEditorInspectorWindow.ed.gui"); + //EWInspectorWindow.resize(getWord(EWInspectorWindow.Position, 0), getWord(EWInspectorWindow.Position, 1), getWord(EWInspectorWindow.extent, 0), getWord(EWInspectorWindow.extent, 1)); + if( isObject( EWInspectorWindow ) ) + { + %this.add( EWInspectorWindow ); + EWInspectorWindow.setVisible( false ); + } + } + + if( !isObject( %this-->WorldEditorToolbar ) ) + { + // Load Creator/Inspector GUI + exec("~/worldEditor/gui/WorldEditorToolbar.ed.gui"); + if( isObject( EWorldEditorToolbar ) ) + { + %this.add( EWorldEditorToolbar ); + EWorldEditorToolbar.setVisible( false ); + } + } + + if ( !isObject( %this-->TerrainEditToolbar ) ) + { + // Load Terrain Edit GUI + exec("~/worldEditor/gui/TerrainEditToolbar.ed.gui"); + if( isObject( EWTerrainEditToolbar ) ) + { + %this.add( EWTerrainEditToolbar ); + EWTerrainEditToolbar.setVisible( false ); + } + } + + if( !isObject( %this-->TerrainPainter ) ) + { + // Load Terrain Painter GUI + exec("~/worldEditor/gui/TerrainPainterWindow.ed.gui"); + if( isObject( %guiContent ) ){ + %this.add( %guiContent->TerrainPainter ); + %this.add( %guiContent->TerrainPainterPreview ); + } + + exec("~/worldEditor/gui/guiTerrainMaterialDlg.ed.gui"); + exec("~/worldEditor/gui/TerrainBrushSoftnessCurveDlg.ed.gui"); + } + if ( !isObject( %this-->TerrainPainterToolbar) ) + { + // Load Terrain Edit GUI + exec("~/worldEditor/gui/TerrainPainterToolbar.ed.gui"); + if( isObject( EWTerrainPainterToolbar ) ) + { + %this.add( EWTerrainPainterToolbar ); + EWTerrainPainterToolbar.setVisible( false ); + } + } + + if( !isObject( %this-->ToolsToolbar ) ) + { + // Load Creator/Inspector GUI + exec("~/worldEditor/gui/ToolsToolbar.ed.gui"); + if( isObject( EWToolsToolbar ) ) + { + %this.add( EWToolsToolbar ); + EWToolsToolbar.setVisible( true ); + + } + } + + // Visibility Layer Window + if( !isObject( %this-->VisibilityLayerWindow ) ) + { + %this.add( EVisibility ); + EVisibility.setVisible(false); + EVisibilityTabBook.selectPage(0); + } + + // Editor Settings Window + if( !isObject( %this-->EditorSettingsWindow ) ) + { + exec("~/worldEditor/gui/EditorSettingsWindow.ed.gui"); + exec("~/worldEditor/scripts/editorSettingsWindow.ed.cs"); + %this.add( ESettingsWindow ); + ESettingsWindow.setVisible(false); + + // Start the standard settings tabs pages + exec( "~/worldEditor/gui/GeneralSettingsTab.ed.gui" ); + ESettingsWindow.addTabPage( EGeneralSettingsPage ); + exec("~/worldEditor/gui/ObjectEditorSettingsTab.ed.gui"); + ESettingsWindow.addTabPage( EObjectEditorSettingsPage ); + exec("~/worldEditor/gui/AxisGizmoSettingsTab.ed.gui"); + ESettingsWindow.addTabPage( EAxisGizmoSettingsPage ); + exec("~/worldEditor/gui/TerrainEditorSettingsTab.ed.gui"); + ESettingsWindow.addTabPage( ETerrainEditorSettingsPage ); + exec("~/worldEditor/gui/CameraSettingsTab.ed.gui"); + ESettingsWindow.addTabPage( ECameraSettingsPage ); + } + + // Object Snap Options Window + if( !isObject( %this-->SnapOptionsWindow ) ) + { + exec("~/worldEditor/gui/ObjectSnapOptionsWindow.ed.gui"); + exec("~/worldEditor/scripts/objectSnapOptions.ed.cs"); + %this.add( ESnapOptions ); + ESnapOptions.setVisible(false); + ESnapOptionsTabBook.selectPage(0); + } + + // Transform Selection Window + if( !isObject( %this-->TransformSelectionWindow ) ) + { + exec("~/worldEditor/gui/TransformSelectionWindow.ed.gui"); + exec("~/worldEditor/scripts/transformSelection.ed.cs"); + %this.add( ETransformSelection ); + ETransformSelection.setVisible(false); + } + + // Manage Bookmarks Window + if( !isObject( %this-->ManageBookmarksWindow ) ) + { + %this.add( EManageBookmarks ); + EManageBookmarks.setVisible(false); + } + + // Manage SFXParameters Window + if( !isObject( %this-->ManageSFXParametersWindow ) ) + { + %this.add( EManageSFXParameters ); + EManageSFXParameters.setVisible( false ); + } + + // Select Objects Window + if( !isObject( %this->SelectObjectsWindow ) ) + { + %this.add( ESelectObjectsWindow ); + ESelectObjectsWindow.setVisible( false ); + } + + EWorldEditor.init(); + ETerrainEditor.init(); + + //Creator.init(); + EWCreatorWindow.init(); + ObjectBuilderGui.init(); + + %this.setMenuDefaultState(); + + EWorldEditorToggleCamera.setBitmap("tools/worldEditor/images/toolbar/player"); + + /* + EWorldEditorCameraSpeed.clear(); + EWorldEditorCameraSpeed.add("Slowest - Camera 1",0); + EWorldEditorCameraSpeed.add("Slow - Camera 2",1); + EWorldEditorCameraSpeed.add("Slower - Camera 3",2); + EWorldEditorCameraSpeed.add("Normal - Camera 4",3); + EWorldEditorCameraSpeed.add("Faster - Camera 5",4); + EWorldEditorCameraSpeed.add("Fast - Camera 6",5); + EWorldEditorCameraSpeed.add("Fastest - Camera 7",6); + EWorldEditorCameraSpeed.setSelected(3); + */ + + EWorldEditorAlignPopup.clear(); + EWorldEditorAlignPopup.add("World",0); + EWorldEditorAlignPopup.add("Object",1); + EWorldEditorAlignPopup.setSelected(0); + + + // sync camera gui + EditorGui.syncCameraGui(); + + // this will brind CameraTypesDropdown to front so that it goes over the menubar + EditorGui.pushToBack(CameraTypesDropdown); + EditorGui.pushToBack(VisibilityDropdown); + + // dropdowns out so that they display correctly in editor gui + objectTransformDropdown.parentGroup = editorGui; + objectCenterDropdown.parentGroup = editorGui; + objectSnapDropdown.parentGroup = editorGui; + + // make sure to show the default world editor guis + EditorGui.bringToFront( EWorldEditor ); + EWorldEditor.setVisible( false ); + + // Call the startup callback on the editor plugins. + for ( %i = 0; %i < EditorPluginSet.getCount(); %i++ ) + { + %obj = EditorPluginSet.getObject( %i ); + %obj.onWorldEditorStartup(); + } + + // With everything loaded, start up the settings window + ESettingsWindow.startup(); + + // Start up initial editor plugin. + + %initialEditor = %this.currentEditor; // Read from prefs. + %this.currentEditor = ""; + + if( %initialEditor $= "" ) + %initialEditor = "WorldEditorInspectorPlugin"; + %this.setEditor( %initialEditor, true, true ); + + // Done. + + %this.isInitialized = true; +} + +//------------------------------------------------------------------------------ +// Editor Gui's interactions with Camera Settings + +function EditorGui::setupDefaultCameraSettings( %this ) +{ + EditorSettings.beginGroup( "LevelInformation/levels/" @ %this.levelName ); + + EditorSettings.setDefaultValue( "cameraSpeedMin", "5" ); + EditorSettings.setDefaultValue( "cameraSpeedMax", "200" ); + + EditorSettings.endGroup(); +} + +function EditorGui::readCameraSettings( %this, %levelName ) +{ + if( %levelName !$= %this.levelName ) + return; + + EditorCameraSpeedOptions.setupGuiControls(); +} + +function EditorGui::writeCameraSettings( %this ) +{ + EditorSettings.beginGroup( "LevelInformation/levels/" @ %this.levelName ); + + EditorSettings.setValue( "cameraSpeed", $Camera::movementSpeed ); + + EditorSettings.endGroup(); +} + +//------------------------------------------------------------------------------ + +function EditorGui::shutdown( %this ) +{ + // Store settings. + %this.writeWorldEditorSettings(); + + // Deactivate current editor. + %this.setEditor( "" ); + + // Call the shutdown callback on the editor plugins. + foreach( %plugin in EditorPluginSet ) + %plugin.onWorldEditorShutdown(); +} + +/// This is used to add an editor to the Editors menu which +/// will take over the default world editor window. +function EditorGui::addToEditorsMenu( %this, %displayName, %accel, %newPlugin ) +{ + %windowMenu = %this.findMenu( "Editors" ); + %count = %windowMenu.getItemCount(); + + + %alreadyExists = false; + for ( %i = 0; %i < %count; %i++ ) + { + %existingPlugins = getField(%windowMenu.Item[%i], 2); + + if(%newPlugin $= %existingPlugins) + %alreadyExists = true; + } + + if( %accel $= "" && %count < 9 ) + %accel = "F" @ %count + 1; + else + %accel = ""; + + if(!%alreadyExists) + %windowMenu.addItem( %count, %displayName TAB %accel TAB %newPlugin ); + + return %accel; +} + +function EditorGui::addToToolsToolbar( %this, %pluginName, %internalName, %bitmap, %tooltip ) +{ + %count = ToolsToolbarArray.getCount(); + + %alreadyExists = false; + for ( %i = 0; %i < %count; %i++ ) + { + %existingInternalName = ToolsToolbarArray.getObject(%i).getFieldValue("internalName"); + + if(%internalName $= %existingInternalName) + { + %alreadyExists = true; + break; + } + } + + if(!%alreadyExists) + { + %button = new GuiBitmapButtonCtrl() { + canSaveDynamicFields = "0"; + internalName = %internalName; + Enabled = "1"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "180 0"; + Extent = "25 19"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = "EditorGui.setEditor(" @ %pluginName @ ");"; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = %tooltip; + hovertime = "750"; + bitmap = %bitmap; + buttonType = "RadioButton"; + groupNum = "0"; + useMouseEvents = "0"; + }; + ToolsToolbarArray.add(%button); + EWToolsToolbar.setExtent((25 + 8) * (%count + 1) + 12 SPC "33"); + } +} + +//----------------------------------------------------------------------------- + +function EditorGui::setDisplayType( %this, %type ) +{ + %gui = %this.currentEditor.editorGui; + if( !isObject( %gui ) ) + return; + + %this.viewTypeMenu.checkRadioItem( 0, 7, %type ); + + // Store the current camera rotation so we can restore it correctly when + // switching back to perspective view + if ( %gui.getDisplayType() == $EditTSCtrl::DisplayTypePerspective ) + %this.lastPerspectiveCamRotation = LocalClientConnection.camera.getRotation(); + + %gui.setDisplayType( %type ); + + if ( %gui.getDisplayType() == $EditTSCtrl::DisplayTypePerspective ) + LocalClientConnection.camera.setRotation( %this.lastPerspectiveCamRotation ); + + %this.currentDisplayType = %type; +} + +//----------------------------------------------------------------------------- + +function EditorGui::setEditor( %this, %newEditor, %dontActivate ) +{ + if ( isObject( %this.currentEditor ) ) + { + if( isObject( %newEditor ) && %this.currentEditor.getId() == %newEditor.getId() ) + return; + + if( %this.currentEditor.isActivated ) + %this.currentEditor.onDeactivated(); + + if( isObject( %this.currentEditor.editorGui ) ) + %this.currentOrthoFOV = %this.currentEditor.editorGui.getOrthoFOV(); + } + + if( !isObject( %newEditor ) ) + { + %this.currentEditor = ""; + return; + } + + // If we have a special set editor function, run that instead + if( %newEditor.isMethod( "setEditorFunction" ) ) + { + if( %newEditor.setEditorFunction() ) + { + %this.syncEditor( %newEditor ); + %this.currentEditor = %newEditor; + + if (!%dontActivate) + %this.currentEditor.onActivated(); + } + else + { + // if were falling back and were the same editor, why are we going to just shove ourself + // into the editor position again? opt for a fallback + if( !isObject( %this.currentEditor ) ) + %this.currentEditor = "WorldEditorInspectorPlugin"; + else if( %this.currentEditor.getId() == %newEditor.getId() ) + %this.currentEditor = "WorldEditorInspectorPlugin"; + + %this.syncEditor( %this.currentEditor, true ); + + if( !%dontActivate ) + %this.currentEditor.onActivated(); + } + } + else + { + %this.syncEditor( %newEditor ); + %this.currentEditor = %newEditor; + + if( !%dontActivate ) + %this.currentEditor.onActivated(); + } + + // Sync display type. + + %gui = %this.currentEditor.editorGui; + if( isObject( %gui ) ) + { + %gui.setDisplayType( %this.currentDisplayType ); + %gui.setOrthoFOV( %this.currentOrthoFOV ); + EditorGui.syncCameraGui(); + } +} + +function EditorGui::syncEditor( %this, %newEditor, %newEditorFailed ) +{ + // Sync with menu bar + %menu = %this.findMenu( "Editors" ); + %count = %menu.getItemCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %pluginObj = getField( %menu.item[%i], 2 ); + if ( %pluginObj $= %newEditor ) + { + %menu.checkRadioItem( 0, %count, %i ); + break; + } + } + + // In order to hook up a palette, the word Palette must be able to be + // switched out in order to read correctly, if not, no palette will be used + %paletteName = strreplace(%newEditor, "Plugin", "Palette"); + + // Sync with ToolsToolbar + for ( %i = 0; %i < ToolsToolbarArray.getCount(); %i++ ) + { + %toolbarButton = ToolsToolbarArray.getObject(%i).internalName; + if( %paletteName $= %toolbarButton ) + { + ToolsToolbarArray.getObject(%i).setStateOn(1); + break; + } + } + + // Handles quit game and gui editor changes in wierd scenarios + if( %newEditorFailed && EWToolsToolbar.isDynamic ) + { + if( EWToolsToolbar.isClosed ) + EWToolsToolbar.reset(); + EWToolsToolbar.toggleSize(); + } + + // Toggle the editor specific palette; we define special cases here + switch$ ( %paletteName ) + { + case "MaterialEditorPalette": + %paletteName = "WorldEditorInspectorPalette"; + case "DatablockEditorPalette": + %paletteName = "WorldEditorInspectorPalette"; + case "ParticleEditorPalette": + %paletteName = "WorldEditorInspectorPalette"; + } + + %this-->ToolsPaletteWindow.togglePalette(%paletteName); +} + +function EditorGui::onWake( %this ) +{ + EHWorldEditor.setStateOn( 1 ); + + // Notify the editor plugins that the editor has started. + + foreach( %plugin in EditorPluginSet ) + %plugin.onEditorWake(); + + // Push the ActionMaps in the order that we want to have them + // before activating an editor plugin, so that if the plugin + // installs an ActionMap, it will be highest on the stack. + + MoveMap.push(); + EditorMap.push(); + + // Active the current editor plugin. + + if( !%this.currentEditor.isActivated ) + %this.currentEditor.onActivated(); + + %slashPos = 0; + while( strpos( $Server::MissionFile, "/", %slashPos ) != -1 ) + { + %slashPos = strpos( $Server::MissionFile, "/", %slashPos ) + 1; + } + %levelName = getSubStr( $Server::MissionFile , %slashPos , 99 ); + + if( %levelName !$= %this.levelName ) + %this.onNewLevelLoaded( %levelName ); +} + +function EditorGui::onSleep( %this ) +{ + // Deactivate the current editor plugin. + + if( %this.currentEditor.isActivated ) + %this.currentEditor.onDeactivated(); + + // Remove the editor's ActionMaps. + + EditorMap.pop(); + MoveMap.pop(); + + // Notify the editor plugins that the editor will be closing. + + foreach( %plugin in EditorPluginSet ) + %plugin.onEditorSleep(); + + if(isObject($Server::CurrentScene)) + $Server::CurrentScene.open(); +} + +function EditorGui::onNewLevelLoaded( %this, %levelName ) +{ + %this.levelName = %levelName; + %this.setupDefaultCameraSettings(); + ECameraSettingsPage.init(); + EditorCameraSpeedOptions.setupDefaultState(); + + new ScriptObject( EditorMissionCleanup ) + { + parentGroup = "MissionCleanup"; + }; +} + +function EditorMissionCleanup::onRemove( %this ) +{ + EditorGui.levelName = ""; + foreach( %plugin in EditorPluginSet ) + %plugin.onExitMission(); +} + +//----------------------------------------------------------------------------- + +// Called when we have been set as the content and onWake has been called +function EditorGui::onSetContent(%this, %oldContent) +{ + %this.attachMenus(); +} + +// Called before onSleep when the canvas content is changed +function EditorGui::onUnsetContent(%this, %newContent) +{ + %this.detachMenus(); +} + +//------------------------------------------------------------------------------ + +function EditorGui::toggleSFXParametersWindow( %this ) +{ + %window = %this-->ManageSFXParametersWindow; + %window.setVisible( !%window.isVisible() ); +} + +//------------------------------------------------------------------------------ + +function EditorGui::addCameraBookmark( %this, %name ) +{ + %obj = new CameraBookmark() { + datablock = CameraBookmarkMarker; + internalName = %name; + }; + + // Place into correct group + if( !isObject(CameraBookmarks) ) + { + %grp = new SimGroup(CameraBookmarks); + MissionGroup.add(%grp); + } + CameraBookmarks.add( %obj ); + + %cam = LocalClientConnection.camera.getTransform(); + %obj.setTransform( %cam ); + + EWorldEditor.isDirty = true; + EditorTree.buildVisibleTree(true); +} + +function EditorGui::removeCameraBookmark( %this, %name ) +{ + if( !isObject(CameraBookmarks) ) + return; + + %mark = CameraBookmarks.findObjectByInternalName( %name, true ); + if( %mark == 0 ) + return; + + MEDeleteUndoAction::submit( %mark ); + EWorldEditor.isDirty = true; + EditorTree.buildVisibleTree(true); +} + +function EditorGui::removeCameraBookmarkIndex( %this, %index ) +{ + if( !isObject(CameraBookmarks) ) + return; + + if( %index < 0 || %index >= CameraBookmarks.getCount() ) + return; + + %obj = CameraBookmarks.getObject( %index ); + MEDeleteUndoAction::submit( %obj ); + EWorldEditor.isDirty = true; + EditorTree.buildVisibleTree(true); +} + +function EditorGui::jumpToBookmark( %this, %name ) +{ + if( !isObject(CameraBookmarks) ) + return; + + %mark = CameraBookmarks.findObjectByInternalName( %name, true ); + if( %mark == 0 ) + return; + + LocalClientConnection.camera.setTransform( %mark.getTransform() ); + return; +} + +function EditorGui::jumpToBookmarkIndex( %this, %index ) +{ + if( !isObject(CameraBookmarks) ) + return; + + if( %index < 0 || %index >= CameraBookmarks.getCount() ) + return; + + %trans = CameraBookmarks.getObject( %index ).getTransform(); + LocalClientConnection.camera.setTransform( %trans ); +} + +function EditorGui::addCameraBookmarkByGui( %this ) +{ + // look for a NewCamera name to grab + for(%i = 0; ; %i++){ + %name = "NewCamera_" @ %i; + if( !CameraBookmarks.findObjectByInternalName(%name) ){ + break; + } + } + EditorGui.addCameraBookmark( %name ); +} + +function EditorGui::toggleCameraBookmarkWindow( %this ) +{ + EManageBookmarks.ToggleVisibility(); +} + +function EditorGui::toggleObjectSelectionsWindow( %this ) +{ + ESelectObjectsWindow.toggleVisibility(); +} + +function EditorGui::toggleOrthoGrid( %this ) +{ + EWorldEditor.renderOrthoGrid = !EWorldEditor.renderOrthoGrid; +} + +//------------------------------------------------------------------------------ + +function EditorGui::syncCameraGui( %this ) +{ + if( !EditorIsActive() ) + return; + + // Sync projection type + %displayType = %this.currentEditor.editorGui.getDisplayType(); + %this.viewTypeMenu.checkRadioItem( 0, 7, %displayType ); + + // Set the camera object's mode and rotation so that it moves correctly + // based on the current editor mode + if( %displayType != $EditTSCtrl::DisplayTypePerspective ) + { + switch( %displayType ) + { + case $EditTSCtrl::DisplayTypeTop: %name = "Top View"; %camRot = "0 0 0"; + case $EditTSCtrl::DisplayTypeBottom: %name = "Bottom View"; %camRot = "3.14159 0 0"; + case $EditTSCtrl::DisplayTypeLeft: %name = "Left View"; %camRot = "-1.571 0 1.571"; + case $EditTSCtrl::DisplayTypeRight: %name = "Right View"; %camRot = "-1.571 0 -1.571"; + case $EditTSCtrl::DisplayTypeFront: %name = "Front View"; %camRot = "-1.571 0 3.14159"; + case $EditTSCtrl::DisplayTypeBack: %name = "Back View"; %camRot = "-1.571 0 0"; + case $EditTSCtrl::DisplayTypeIsometric: %name = "Isometric View"; %camRot = "0 0 0"; + } + + LocalClientConnection.camera.controlMode = "Fly"; + LocalClientConnection.camera.setRotation( %camRot ); + EditorGuiStatusBar.setCamera( %name ); + return; + } + + // Sync camera settings. + %flyModeRadioItem = -1; + if(LocalClientConnection.getControlObject() != LocalClientConnection.player) + { + %mode = LocalClientConnection.camera.getMode(); + + if(%mode $= "Fly" && LocalClientConnection.camera.newtonMode) + { + if(LocalClientConnection.camera.newtonRotation == true) + { + EditorGui-->NewtonianRotationCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/gui/images/menubar/smooth-cam-rot"); + %flyModeRadioItem = 4; + EditorGuiStatusBar.setCamera("Smooth Rot Camera"); + } + else + { + EditorGui-->NewtonianCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/gui/images/menubar/smooth-cam"); + %flyModeRadioItem = 3; + EditorGuiStatusBar.setCamera("Smooth Camera"); + } + } + else if(%mode $= "EditOrbit") + { + EditorGui-->OrbitCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/gui/images/menubar/orbit-cam"); + %flyModeRadioItem = 1; + EditorGuiStatusBar.setCamera("Orbit Camera"); + } + else // default camera mode + { + EditorGui-->StandardCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/worldEditor/images/toolbar/camera"); + %flyModeRadioItem = 0; + EditorGuiStatusBar.setCamera("Standard Camera"); + } + + //quick way select menu bar options + %this.findMenu( "Camera" ).checkRadioItem( 0, 1, 0 ); + EditorFreeCameraTypeOptions.checkRadioItem( 0, 4, %flyModeRadioItem); + EditorPlayerCameraTypeOptions.checkRadioItem( 0, 4, -1); + } + else if (!$isFirstPersonVar) // if 3rd person + { + EditorGui-->trdPersonCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/worldEditor/images/toolbar/3rd-person-camera"); + %flyModeRadioItem = 1; + //quick way select menu bar options + %this.findMenu( "Camera" ).checkRadioItem( 0, 1, 1 ); + EditorPlayerCameraTypeOptions.checkRadioItem( 0, 2, %flyModeRadioItem); + EditorGuiStatusBar.setCamera("3rd Person Camera"); + } + else if ($isFirstPersonVar) // if 1st Person + { + EditorGui-->PlayerCamera.setStateOn(true); + EWorldEditorToggleCamera.setBitmap("tools/worldEditor/images/toolbar/player"); + %flyModeRadioItem = 0; + //quick way select menu bar options + %this.findMenu( "Camera" ).checkRadioItem( 0, 1, 1 ); + EditorPlayerCameraTypeOptions.checkRadioItem( 0, 2, %flyModeRadioItem); + EditorFreeCameraTypeOptions.checkRadioItem( 0, 4, -1); + EditorGuiStatusBar.setCamera("1st Person Camera"); + } + } + +/// @name EditorPlugin Methods +/// @{ + +//------------------------------------------------------------------------------ +// WorldEditorPlugin +//------------------------------------------------------------------------------ + +function WorldEditorPlugin::onActivated( %this ) +{ + EditorGui.bringToFront( EWorldEditor ); + EWorldEditor.setVisible(true); + EditorGui.menuBar.insert( EditorGui.worldMenu, EditorGui.menuBar.dynamicItemInsertPos ); + EWorldEditor.makeFirstResponder(true); + EditorTree.open(MissionGroup,true); + EWCreatorWindow.setNewObjectGroup(MissionGroup); + + EWorldEditor.syncGui(); + + EditorGuiStatusBar.setSelectionObjectsByCount(EWorldEditor.getSelectionSize()); + + // Should the Transform Selection window open? + if( EWorldEditor.ETransformSelectionDisplayed ) + { + ETransformSelection.setVisible(true); + } + + Parent::onActivated(%this); +} + +function WorldEditorPlugin::onDeactivated( %this ) +{ + // Hide the Transform Selection window from other editors + ETransformSelection.setVisible(false); + + EWorldEditor.setVisible( false ); + EditorGui.menuBar.remove( EditorGui.worldMenu ); + + Parent::onDeactivated(%this); +} + +//------------------------------------------------------------------------------ +// WorldEditorInspectorPlugin +//------------------------------------------------------------------------------ + +function WorldEditorInspectorPlugin::onWorldEditorStartup( %this ) +{ + Parent::onWorldEditorStartup( %this ); + + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Object Editor", "", WorldEditorInspectorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Object Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "WorldEditorInspectorPlugin", "WorldEditorInspectorPalette", expandFilename("tools/worldEditor/images/toolbar/transform-objects"), %tooltip ); + + //connect editor windows + GuiWindowCtrl::attach( EWInspectorWindow, EWTreeWindow); + + %map = new ActionMap(); + %map.bindCmd( keyboard, "1", "EWorldEditorNoneModeBtn.performClick();", "" ); // Select + %map.bindCmd( keyboard, "2", "EWorldEditorMoveModeBtn.performClick();", "" ); // Move + %map.bindCmd( keyboard, "3", "EWorldEditorRotateModeBtn.performClick();", "" ); // Rotate + %map.bindCmd( keyboard, "4", "EWorldEditorScaleModeBtn.performClick();", "" ); // Scale + %map.bindCmd( keyboard, "f", "FitToSelectionBtn.performClick();", "" );// Fit Camera to Selection + %map.bindCmd( keyboard, "z", "EditorGuiStatusBar.setCamera(\"Standard Camera\");", "" );// Free camera + %map.bindCmd( keyboard, "n", "ToggleNodeBar->renderHandleBtn.performClick();", "" );// Render Node + %map.bindCmd( keyboard, "shift n", "ToggleNodeBar->renderTextBtn.performClick();", "" );// Render Node Text + %map.bindCmd( keyboard, "g", "ESnapOptions-->GridSnapButton.performClick();" ); // Grid Snappping + %map.bindCmd( keyboard, "t", "SnapToBar->objectSnapDownBtn.performClick();", "" );// Terrain Snapping + %map.bindCmd( keyboard, "b", "SnapToBar-->objectSnapBtn.performClick();" ); // Soft Snappping + %map.bindCmd( keyboard, "v", "EWorldEditorToolbar->boundingBoxColBtn.performClick();", "" );// Bounds Selection + %map.bindCmd( keyboard, "o", "objectCenterDropdown->objectBoxBtn.performClick(); objectCenterDropdown.toggle();", "" );// Object Center + %map.bindCmd( keyboard, "p", "objectCenterDropdown->objectBoundsBtn.performClick(); objectCenterDropdown.toggle();", "" );// Bounds Center + %map.bindCmd( keyboard, "k", "objectTransformDropdown->objectTransformBtn.performClick(); objectTransformDropdown.toggle();", "" );// Object Transform + %map.bindCmd( keyboard, "l", "objectTransformDropdown->worldTransformBtn.performClick(); objectTransformDropdown.toggle();", "" );// World Transform + + WorldEditorInspectorPlugin.map = %map; +} + +function WorldEditorInspectorPlugin::onActivated( %this ) +{ + Parent::onActivated( %this ); + + EditorGui-->InspectorWindow.setVisible( true ); + EditorGui-->TreeWindow.setVisible( true ); + EditorGui-->WorldEditorToolbar.setVisible( true ); + %this.map.push(); +} + +function WorldEditorInspectorPlugin::onDeactivated( %this ) +{ + Parent::onDeactivated( %this ); + + EditorGui-->InspectorWindow.setVisible( false ); + EditorGui-->TreeWindow.setVisible( false ); + EditorGui-->WorldEditorToolbar.setVisible( false ); + %this.map.pop(); +} + +function WorldEditorInspectorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %canCutCopy = EWorldEditor.getSelectionSize() > 0; + %editMenu.enableItem( 3, %canCutCopy ); // Cut + %editMenu.enableItem( 4, %canCutCopy ); // Copy + %editMenu.enableItem( 5, EWorldEditor.canPasteSelection() ); // Paste + + %selSize = EWorldEditor.getSelectionSize(); + %lockCount = EWorldEditor.getSelectionLockCount(); + %hideCount = EWorldEditor.getSelectionHiddenCount(); + %editMenu.enableItem( 6, %selSize > 0 && %lockCount != %selSize ); // Delete Selection + + %editMenu.enableItem( 8, %canCutCopy ); // Deselect +} + +function WorldEditorInspectorPlugin::handleDelete( %this ) +{ + // The tree handles deletion and notifies the + // world editor to clear its selection. + // + // This is because non-SceneObject elements like + // SimGroups also need to be destroyed. + // + // See EditorTree::onObjectDeleteCompleted(). + %selSize = EWorldEditor.getSelectionSize(); + if( %selSize > 0 ) + EditorTree.deleteSelection(); +} + +function WorldEditorInspectorPlugin::handleDeselect() +{ + EWorldEditor.clearSelection(); +} + +function WorldEditorInspectorPlugin::handleCut() +{ + EWorldEditor.cutSelection(); +} + +function WorldEditorInspectorPlugin::handleCopy() +{ + EWorldEditor.copySelection(); +} + +function WorldEditorInspectorPlugin::handlePaste() +{ + EWorldEditor.pasteSelection(); +} + +//------------------------------------------------------------------------------ +// TerrainEditorPlugin +//------------------------------------------------------------------------------ + +function TerrainEditorPlugin::onWorldEditorStartup( %this ) +{ + Parent::onWorldEditorStartup( %this ); + + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Terrain Editor", "", TerrainEditorPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Terrain Editor (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "TerrainEditorPlugin", "TerrainEditorPalette", expandFilename("tools/worldEditor/images/toolbar/sculpt-terrain"), %tooltip ); + + %map = new ActionMap(); + %map.bindCmd( keyboard, "1", "ToolsPaletteArray->brushAdjustHeight.performClick();", "" ); //Grab Terrain + %map.bindCmd( keyboard, "2", "ToolsPaletteArray->raiseHeight.performClick();", "" ); // Raise Height + %map.bindCmd( keyboard, "3", "ToolsPaletteArray->lowerHeight.performClick();", "" ); // Lower Height + %map.bindCmd( keyboard, "4", "ToolsPaletteArray->smoothHeight.performClick();", "" ); // Average Height + %map.bindCmd( keyboard, "5", "ToolsPaletteArray->smoothSlope.performClick();", "" ); // Smooth Slope + %map.bindCmd( keyboard, "6", "ToolsPaletteArray->paintNoise.performClick();", "" ); // Noise + %map.bindCmd( keyboard, "7", "ToolsPaletteArray->flattenHeight.performClick();", "" ); // Flatten + %map.bindCmd( keyboard, "8", "ToolsPaletteArray->setHeight.performClick();", "" ); // Set Height + %map.bindCmd( keyboard, "9", "ToolsPaletteArray->setEmpty.performClick();", "" ); // Clear Terrain + %map.bindCmd( keyboard, "0", "ToolsPaletteArray->clearEmpty.performClick();", "" ); // Restore Terrain + %map.bindCmd( keyboard, "v", "EWTerrainEditToolbarBrushType->ellipse.performClick();", "" );// Circle Brush + %map.bindCmd( keyboard, "b", "EWTerrainEditToolbarBrushType->box.performClick();", "" );// Box Brush + %map.bindCmd( keyboard, "=", "TerrainEditorPlugin.keyboardModifyBrushSize(1);", "" );// +1 Brush Size + %map.bindCmd( keyboard, "+", "TerrainEditorPlugin.keyboardModifyBrushSize(1);", "" );// +1 Brush Size + %map.bindCmd( keyboard, "-", "TerrainEditorPlugin.keyboardModifyBrushSize(-1);", "" );// -1 Brush Size + %map.bindCmd( keyboard, "[", "TerrainEditorPlugin.keyboardModifyBrushSize(-5);", "" );// -5 Brush Size + %map.bindCmd( keyboard, "]", "TerrainEditorPlugin.keyboardModifyBrushSize(5);", "" );// +5 Brush Size + /*%map.bindCmd( keyboard, "]", "TerrainBrushPressureTextEditContainer->textEdit.text += 5", "" );// +5 Pressure + %map.bindCmd( keyboard, "[", "TerrainBrushPressureTextEditContainer->textEdit.text -= 5", "" );// -5 Pressure + %map.bindCmd( keyboard, "'", "TerrainBrushSoftnessTextEditContainer->textEdit.text += 5", "" );// +5 Softness + %map.bindCmd( keyboard, ";", "TerrainBrushSoftnessTextEditContainer->textEdit.text -= 5", "" );// -5 Softness*/ + + TerrainEditorPlugin.map = %map; + + %this.terrainMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + + barTitle = "Terrain"; + + item[0] = "Smooth Heightmap" TAB "" TAB "ETerrainEditor.onSmoothHeightmap();"; + }; +} + +function TerrainEditorPlugin::onActivated( %this ) +{ + Parent::onActivated( %this ); + + EditorGui.readTerrainEditorSettings(); + + %action = EditorSettings.value("TerrainEditor/currentAction"); + ETerrainEditor.switchAction( %action ); + ToolsPaletteArray.findObjectByInternalName( %action, true ).setStateOn( true ); + + EWTerrainEditToolbarBrushType->ellipse.performClick(); // Circle Brush + + EditorGui.menuBar.insert( %this.terrainMenu, EditorGui.menuBar.dynamicItemInsertPos ); + + EditorGui.bringToFront( ETerrainEditor ); + ETerrainEditor.setVisible( true ); + ETerrainEditor.attachTerrain(); + ETerrainEditor.makeFirstResponder( true ); + + EWTerrainEditToolbar.setVisible( true ); + ETerrainEditor.onBrushChanged(); + ETerrainEditor.setup(); + TerrainEditorPlugin.syncBrushInfo(); + + EditorGuiStatusBar.setSelection(""); + %this.map.push(); +} + +function TerrainEditorPlugin::onDeactivated( %this ) +{ + Parent::onDeactivated( %this ); + + endToolTime("TerrainEditor"); + EditorGui.writeTerrainEditorSettings(); + + EWTerrainEditToolbar.setVisible( false ); + ETerrainEditor.setVisible( false ); + + EditorGui.menuBar.remove( %this.terrainMenu ); + + %this.map.pop(); +} + +function TerrainEditorPlugin::syncBrushInfo( %this ) +{ + // Update gui brush info + TerrainBrushSizeTextEditContainer-->textEdit.text = getWord(ETerrainEditor.getBrushSize(), 0); + TerrainBrushPressureTextEditContainer-->textEdit.text = ETerrainEditor.getBrushPressure()*100; + TerrainBrushSoftnessTextEditContainer-->textEdit.text = ETerrainEditor.getBrushSoftness()*100; + TerrainSetHeightTextEditContainer-->textEdit.text = ETerrainEditor.setHeightVal; + + %brushType = ETerrainEditor.getBrushType(); + eval( "EWTerrainEditToolbar-->" @ %brushType @ ".setStateOn(1);" ); +} + +function TerrainEditorPlugin::validateBrushSize( %this ) +{ + %minBrushSize = 1; + %maxBrushSize = getWord(ETerrainEditor.maxBrushSize, 0); + + %val = $ThisControl.getText(); + if(%val < %minBrushSize) + $ThisControl.setValue(%minBrushSize); + else if(%val > %maxBrushSize) + $ThisControl.setValue(%maxBrushSize); +} + +function TerrainEditorPlugin::keyboardModifyBrushSize( %this, %amt) +{ + %val = TerrainBrushSizeTextEditContainer-->textEdit.getText(); + %val += %amt; + TerrainBrushSizeTextEditContainer-->textEdit.setValue(%val); + TerrainBrushSizeTextEditContainer-->textEdit.forceValidateText(); + ETerrainEditor.setBrushSize( TerrainBrushSizeTextEditContainer-->textEdit.getText() ); +} + +//------------------------------------------------------------------------------ +// TerrainTextureEditorTool +//------------------------------------------------------------------------------ + +function TerrainTextureEditorTool::onActivated( %this ) +{ + EditorGui.bringToFront( ETerrainEditor ); + ETerrainEditor.setVisible( true ); + ETerrainEditor.attachTerrain(); + ETerrainEditor.makeFirstResponder( true ); + + EditorGui-->TextureEditor.setVisible(true); + + EditorGuiStatusBar.setSelection(""); +} + +function TerrainTextureEditorTool::onDeactivated( %this ) +{ + EditorGui-->TextureEditor.setVisible(false); + + ETerrainEditor.setVisible( false ); +} + +//------------------------------------------------------------------------------ +// TerrainPainterPlugin +//------------------------------------------------------------------------------ + +function TerrainPainterPlugin::onWorldEditorStartup( %this ) +{ + Parent::onWorldEditorStartup( %this ); + + // Add ourselves to the window menu. + %accel = EditorGui.addToEditorsMenu( "Terrain Painter", "", TerrainPainterPlugin ); + + // Add ourselves to the ToolsToolbar + %tooltip = "Terrain Painter (" @ %accel @ ")"; + EditorGui.addToToolsToolbar( "TerrainPainterPlugin", "TerrainPainterPalette", expandFilename("tools/worldEditor/images/toolbar/paint-terrain"), %tooltip ); + + %map = new ActionMap(); + %map.bindCmd( keyboard, "v", "EWTerrainPainterToolbarBrushType->ellipse.performClick();", "" );// Circle Brush + %map.bindCmd( keyboard, "b", "EWTerrainPainterToolbarBrushType->box.performClick();", "" );// Box Brush + %map.bindCmd( keyboard, "=", "TerrainPainterPlugin.keyboardModifyBrushSize(1);", "" );// +1 Brush Size + %map.bindCmd( keyboard, "+", "TerrainPainterPlugin.keyboardModifyBrushSize(1);", "" );// +1 Brush Size + %map.bindCmd( keyboard, "-", "TerrainPainterPlugin.keyboardModifyBrushSize(-1);", "" );// -1 Brush Size + %map.bindCmd( keyboard, "[", "TerrainPainterPlugin.keyboardModifyBrushSize(-5);", "" );// -5 Brush Size + %map.bindCmd( keyboard, "]", "TerrainPainterPlugin.keyboardModifyBrushSize(5);", "" );// +5 Brush Size + /*%map.bindCmd( keyboard, "]", "PaintBrushSlopeControl->SlopeMinAngle.text += 5", "" );// +5 SlopeMinAngle + %map.bindCmd( keyboard, "[", "PaintBrushSlopeControl->SlopeMinAngle.text -= 5", "" );// -5 SlopeMinAngle + %map.bindCmd( keyboard, "'", "PaintBrushSlopeControl->SlopeMaxAngle.text += 5", "" );// +5 SlopeMaxAngle + %map.bindCmd( keyboard, ";", "PaintBrushSlopeControl->SlopeMaxAngle.text -= 5", "" );// -5 Softness*/ + + for(%i=1; %i<10; %i++) + { + %map.bindCmd( keyboard, %i, "TerrainPainterPlugin.keyboardSetMaterial(" @ (%i-1) @ ");", "" ); + } + %map.bindCmd( keyboard, 0, "TerrainPainterPlugin.keyboardSetMaterial(10);", "" ); + + TerrainPainterPlugin.map = %map; + GuiWindowCtrl::attach( EPainter, EPainterPreview); +} + +function TerrainPainterPlugin::onActivated( %this ) +{ + Parent::onActivated( %this ); + + EditorGui.readTerrainEditorSettings(); + + EWTerrainPainterToolbarBrushType->ellipse.performClick();// Circle Brush + %this.map.push(); + + EditorGui.bringToFront( ETerrainEditor ); + ETerrainEditor.setVisible( true ); + ETerrainEditor.attachTerrain(); + ETerrainEditor.makeFirstResponder( true ); + + EditorGui-->TerrainPainter.setVisible(true); + EditorGui-->TerrainPainterPreview.setVisible(true); + EWTerrainPainterToolbar.setVisible(true); + ETerrainEditor.onBrushChanged(); + EPainter.setup(); + TerrainPainterPlugin.syncBrushInfo(); + + EditorGuiStatusBar.setSelection(""); +} + +function TerrainPainterPlugin::onDeactivated( %this ) +{ + Parent::onDeactivated( %this ); + + EditorGui.writeTerrainEditorSettings(); + + %this.map.pop(); + EditorGui-->TerrainPainter.setVisible(false); + EditorGui-->TerrainPainterPreview.setVisible(false); + EWTerrainPainterToolbar.setVisible(false); + ETerrainEditor.setVisible( false ); +} + +function TerrainPainterPlugin::syncBrushInfo( %this ) +{ + // Update gui brush info + PaintBrushSizeTextEditContainer-->textEdit.text = getWord(ETerrainEditor.getBrushSize(), 0); + PaintBrushSlopeControl-->SlopeMinAngle.text = ETerrainEditor.getSlopeLimitMinAngle(); + PaintBrushSlopeControl-->SlopeMaxAngle.text = ETerrainEditor.getSlopeLimitMaxAngle(); + PaintBrushPressureTextEditContainer-->textEdit.text = ETerrainEditor.getBrushPressure()*100; + %brushType = ETerrainEditor.getBrushType(); + eval( "EWTerrainPainterToolbar-->" @ %brushType @ ".setStateOn(1);" ); +} + +function TerrainPainterPlugin::validateBrushSize( %this ) +{ + %minBrushSize = 1; + %maxBrushSize = getWord(ETerrainEditor.maxBrushSize, 0); + + %val = $ThisControl.getText(); + if(%val < %minBrushSize) + $ThisControl.setValue(%minBrushSize); + else if(%val > %maxBrushSize) + $ThisControl.setValue(%maxBrushSize); +} + +function TerrainPainterPlugin::validateSlopeMaxAngle( %this ) +{ + %maxval = ETerrainEditor.getSlopeLimitMaxAngle(); + PaintBrushSlopeControl-->SlopeMaxAngle.setText(%maxval); +} + +function TerrainPainterPlugin::validateSlopeMinAngle( %this ) +{ + %minval = ETerrainEditor.getSlopeLimitMinAngle(); + PaintBrushSlopeControl-->SlopeMinAngle.setText(%minval); +} + +function TerrainPainterPlugin::keyboardModifyBrushSize( %this, %amt) +{ + %val = PaintBrushSizeTextEditContainer-->textEdit.getText(); + %val += %amt; + PaintBrushSizeTextEditContainer-->textEdit.setValue(%val); + PaintBrushSizeTextEditContainer-->textEdit.forceValidateText(); + ETerrainEditor.setBrushSize( PaintBrushSizeTextEditContainer-->textEdit.getText() ); +} + +function TerrainPainterPlugin::keyboardSetMaterial( %this, %mat) +{ + %name = "EPainterMaterialButton" @ %mat; + %ctrl = EPainter.findObjectByInternalName(%name, true); + if(%ctrl) + { + %ctrl.performClick(); + } +} + +/// @} End of EditorPlugin Methods + + +function objectTransformDropdown::toggle() +{ + if ( objectTransformDropdown.visible ) + { + EWorldEditorToolbar-->objectTransform.setStateOn(false); + objectTransformDropdownDecoy.setVisible(false); + objectTransformDropdownDecoy.setActive(false); + objectTransformDropdown.setVisible(false); + } + else + { + EWorldEditorToolbar-->objectTransform.setStateOn(true); + objectTransformDropdown.setVisible(true); + objectTransformDropdownDecoy.setActive(true); + objectTransformDropdownDecoy.setVisible(true); + } +} + +function CameraTypesDropdownToggle() +{ + if ( CameraTypesDropdown.visible ) + { + EWorldEditorToggleCamera.setStateOn(0); + CameraTypesDropdownDecoy.setVisible(false); + CameraTypesDropdownDecoy.setActive(false); + CameraTypesDropdown.setVisible(false); + } + else + { + CameraTypesDropdown.setVisible(true); + CameraTypesDropdownDecoy.setVisible(true); + CameraTypesDropdownDecoy.setActive(true); + EWorldEditorToggleCamera.setStateOn(1); + } +} + +function VisibilityDropdownToggle() +{ + if ( EVisibility.visible ) + { + EVisibility.setVisible(false); + visibilityToggleBtn.setStateOn(0); + } + else + { + EVisibility.setVisible(true); + visibilityToggleBtn.setStateOn(1); + } +} + +function CameraTypesDropdownDecoy::onMouseLeave() +{ + CameraTypesDropdownToggle(); +} + +//----------------------------------------------------------------------------- + +function EWorldEditor::getGridSnap( %this ) +{ + return %this.gridSnap; +} + +function EWorldEditor::setGridSnap( %this, %value ) +{ + %this.gridSnap = %value; + GlobalGizmoProfile.snapToGrid = %value; + %this.syncGui(); +} + +function EWorldEditor::getGridSize( %this ) +{ + return %this.gridSize; +} + +function EWorldEditor::setGridSize( %this, %value ) +{ + GlobalGizmoProfile.gridSize = %value SPC %value SPC %value; + %this.gridSize = %value; + + %this.syncGui(); +} + +//----------------------------------------------------------------------------- + +function EWorldEditor::areAllSelectedObjectsOfType( %this, %className ) +{ + %activeSelection = %this.getActiveSelection(); + if( !isObject( %activeSelection ) ) + return false; + + %count = %activeSelection.getCount(); + for( %i = 0; %i < %count; %i ++ ) + { + %obj = %activeSelection.getObject( %i ); + if( !%obj.isMemberOfClass( %className ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +function EWorldEditorToggleCamera::toggleBitmap(%this) +{ + %currentImage = %this.bitmap; + + if ( %currentImage $= "tools/worldEditor/images/toolbar/player" ) + %image = "tools/worldEditor/images/toolbar/camera"; + else + %image = "tools/worldEditor/images/toolbar/player"; + + %this.setBitmap( %image ); +} + +function EWorldEditorCameraSpeed::updateMenuBar(%this, %editorBarCtrl) +{ + // Update Toolbar TextEdit + if( %editorBarCtrl.getId() == CameraSpeedDropdownCtrlContainer-->slider.getId() ) + { + %value = %editorBarCtrl.getValue(); + EWorldEditorCameraSpeed.setText( %value ); + $Camera::movementSpeed = %value; + } + + // Update Toolbar Slider + if( %editorBarCtrl.getId() == EWorldEditorCameraSpeed.getId() ) + { + %value = %editorBarCtrl.getText(); + if ( %value !$= "" ) + { + if ( %value <= 0 ) // camera speed must be >= 0 + { + %value = 1; + %editorBarCtrl.setText( %value ); + } + CameraSpeedDropdownCtrlContainer-->slider.setValue( %value ); + $Camera::movementSpeed = %value; + } + } + + // Update Editor + EditorCameraSpeedOptions.checkRadioItem(0, 6, -1); +} + +//----------------------------------------------------------------------------- + +function EWorldEditorAlignPopup::onSelect(%this, %id, %text) +{ + if ( GlobalGizmoProfile.mode $= "Scale" && %text $= "World" ) + { + EWorldEditorAlignPopup.setSelected(1); + return; + } + + GlobalGizmoProfile.alignment = %text; +} + +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- + +function EWorldEditorNoneModeBtn::onClick(%this) +{ + GlobalGizmoProfile.mode = "None"; + + EditorGuiStatusBar.setInfo("Selection arrow."); +} + +function EWorldEditorMoveModeBtn::onClick(%this) +{ + GlobalGizmoProfile.mode = "Move"; + + %cmdCtrl = "CTRL"; + if( $platform $= "macos" ) + %cmdCtrl = "CMD"; + + EditorGuiStatusBar.setInfo( "Move selection. SHIFT while dragging duplicates objects. " @ %cmdCtrl @ " to toggle soft snap. ALT to toggle grid snap." ); +} + +function EWorldEditorRotateModeBtn::onClick(%this) +{ + GlobalGizmoProfile.mode = "Rotate"; + + EditorGuiStatusBar.setInfo("Rotate selection."); +} + +function EWorldEditorScaleModeBtn::onClick(%this) +{ + GlobalGizmoProfile.mode = "Scale"; + + EditorGuiStatusBar.setInfo("Scale selection."); +} + +//----------------------------------------------------------------------------- + +function EditorTree::onDeleteSelection( %this ) +{ + %this.undoDeleteList = ""; +} + +function EditorTree::onDeleteObject( %this, %object ) +{ + // Don't delete locked objects + if( %object.locked ) + return true; + + if( %object == EWCreatorWindow.objectGroup ) + EWCreatorWindow.setNewObjectGroup( MissionGroup ); + + // Append it to our list. + %this.undoDeleteList = %this.undoDeleteList TAB %object; + + // We're gonna delete this ourselves in the + // completion callback. + return true; +} + +function EditorTree::onObjectDeleteCompleted( %this ) +{ + // This can be called when a deletion is attempted but nothing was + // actually deleted ( cannot delete the root of the tree ) so only submit + // the undo if we really deleted something. + if ( %this.undoDeleteList !$= "" ) + MEDeleteUndoAction::submit( %this.undoDeleteList ); + + // Let the world editor know to + // clear its selection. + EWorldEditor.clearSelection(); + EWorldEditor.isDirty = true; +} + +function EditorTree::onClearSelected(%this) +{ + WorldEditor.clearSelection(); +} + +function EditorTree::onInspect(%this, %obj) +{ + Inspector.inspect(%obj); +} + +function EditorTree::toggleLock( %this ) +{ + if( EWTreeWindow-->LockSelection.command $= "EWorldEditor.lockSelection(true); EditorTree.toggleLock();" ) + { + EWTreeWindow-->LockSelection.command = "EWorldEditor.lockSelection(false); EditorTree.toggleLock();"; + EWTreeWindow-->DeleteSelection.command = ""; + } + else + { + EWTreeWindow-->LockSelection.command = "EWorldEditor.lockSelection(true); EditorTree.toggleLock();"; + EWTreeWindow-->DeleteSelection.command = "EditorMenuEditDelete();"; + } +} + +function EditorTree::onAddSelection(%this, %obj, %isLastSelection) +{ + EWorldEditor.selectObject( %obj ); + + %selSize = EWorldEditor.getSelectionSize(); + %lockCount = EWorldEditor.getSelectionLockCount(); + + if( %lockCount < %selSize ) + { + EWTreeWindow-->LockSelection.setStateOn(0); + EWTreeWindow-->LockSelection.command = "EWorldEditor.lockSelection(true); EditorTree.toggleLock();"; + } + else if ( %lockCount > 0 ) + { + EWTreeWindow-->LockSelection.setStateOn(1); + EWTreeWindow-->LockSelection.command = "EWorldEditor.lockSelection(false); EditorTree.toggleLock();"; + } + + if( %selSize > 0 && %lockCount == 0 ) + EWTreeWindow-->DeleteSelection.command = "EditorMenuEditDelete();"; + else + EWTreeWindow-->DeleteSelection.command = ""; + + if( %isLastSelection ) + Inspector.addInspect( %obj ); + else + Inspector.addInspect( %obj, false ); + +} +function EditorTree::onRemoveSelection(%this, %obj) +{ + EWorldEditor.unselectObject(%obj); + Inspector.removeInspect( %obj ); +} +function EditorTree::onSelect(%this, %obj) +{ +} + +function EditorTree::onUnselect(%this, %obj) +{ + EWorldEditor.unselectObject(%obj); +} + +function EditorTree::onDragDropped(%this) +{ + EWorldEditor.isDirty = true; +} + +function EditorTree::onAddGroupSelected(%this, %group) +{ + EWCreatorWindow.setNewObjectGroup(%group); +} + +function EditorTree::onRightMouseUp( %this, %itemId, %mouse, %obj ) +{ + %haveObjectEntries = false; + %haveLockAndHideEntries = true; + + // Handle multi-selection. + if( %this.getSelectedItemsCount() > 1 ) + { + %popup = ETMultiSelectionContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETMultiSelectionContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Delete" TAB "" TAB "EditorMenuEditDelete();"; + item[ 1 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; + }; + } + + // Open context menu if this is a CameraBookmark + else if( %obj.isMemberOfClass( "CameraBookmark" ) ) + { + %popup = ETCameraBookmarkContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETCameraBookmarkContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Go To Bookmark" TAB "" TAB "EditorGui.jumpToBookmark( %this.bookmark.getInternalName() );"; + + bookmark = -1; + }; + + ETCameraBookmarkContextPopup.bookmark = %obj; + } + + // Open context menu if this is set CameraBookmarks group. + else if( %obj.name $= "CameraBookmarks" ) + { + %popup = ETCameraBookmarksGroupContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETCameraBookmarksGroupContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Add Camera Bookmark" TAB "" TAB "EditorGui.addCameraBookmarkByGui();"; + }; + } + + // Open context menu if this is a SimGroup + else if( !%obj.isMemberOfClass( "SceneObject" ) ) + { + %popup = ETSimGroupContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETSimGroupContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Rename" TAB "" TAB "EditorTree.showItemRenameCtrl( EditorTree.findItemByObjectId( %this.object ) );"; + item[ 1 ] = "Delete" TAB "" TAB "EWorldEditor.deleteMissionObject( %this.object );"; + item[ 2 ] = "Inspect" TAB "" TAB "inspectObject( %this.object );"; + item[ 3 ] = "-"; + item[ 4 ] = "Toggle Lock Children" TAB "" TAB "EWorldEditor.toggleLockChildren( %this.object );"; + item[ 5 ] = "Toggle Hide Children" TAB "" TAB "EWorldEditor.toggleHideChildren( %this.object );"; + item[ 6 ] = "-"; + item[ 7 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; + item[ 8 ] = "-"; + item[ 9 ] = "Add New Objects Here" TAB "" TAB "EWCreatorWindow.setNewObjectGroup( %this.object );"; + item[ 10 ] = "Add Children to Selection" TAB "" TAB "EWorldEditor.selectAllObjectsInSet( %this.object, false );"; + item[ 11 ] = "Remove Children from Selection" TAB "" TAB "EWorldEditor.selectAllObjectsInSet( %this.object, true );"; + + object = -1; + }; + + %popup.object = %obj; + + %hasChildren = %obj.getCount() > 0; + %popup.enableItem( 10, %hasChildren ); + %popup.enableItem( 11, %hasChildren ); + + %haveObjectEntries = true; + %haveLockAndHideEntries = false; + } + + // Open generic context menu. + else + { + %popup = ETContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 0 ] = "Rename" TAB "" TAB "EditorTree.showItemRenameCtrl( EditorTree.findItemByObjectId( %this.object ) );"; + item[ 1 ] = "Delete" TAB "" TAB "EWorldEditor.deleteMissionObject( %this.object );"; + item[ 2 ] = "Inspect" TAB "" TAB "inspectObject( %this.object );"; + item[ 3 ] = "-"; + item[ 4 ] = "Locked" TAB "" TAB "%this.object.setLocked( !%this.object.locked ); EWorldEditor.syncGui();"; + item[ 5 ] = "Hidden" TAB "" TAB "EWorldEditor.hideObject( %this.object, !%this.object.hidden ); EWorldEditor.syncGui();"; + item[ 6 ] = "-"; + item[ 7 ] = "Group" TAB "" TAB "EWorldEditor.addSimGroup( true );"; + + object = -1; + }; + + if(%obj.isMemberOfClass("Entity")) + { + %popup = ETEntityContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETEntityContextPopup : ETSimGroupContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 12 ] = "-"; + item[ 13 ] = "Convert to Game Object" TAB "" TAB "EWorldEditor.createGameObject( %this.object );"; + }; + } + + // Specialized version for ConvexShapes. + else if( %obj.isMemberOfClass( "ConvexShape" ) ) + { + %popup = ETConvexShapeContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETConvexShapeContextPopup : ETContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 8 ] = "-"; + item[ 9 ] = "Convert to Zone" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Zone\" );"; + item[ 10 ] = "Convert to Portal" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"Portal\" );"; + item[ 11 ] = "Convert to Occluder" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"OcclusionVolume\" );"; + item[ 12 ] = "Convert to Sound Space" TAB "" TAB "EWorldEditor.convertSelectionToPolyhedralObjects( \"SFXSpace\" );"; + }; + } + + // Specialized version for polyhedral objects. + else if( %obj.isMemberOfClass( "Zone" ) || + %obj.isMemberOfClass( "Portal" ) || + %obj.isMemberOfClass( "OcclusionVolume" ) || + %obj.isMemberOfClass( "SFXSpace" ) ) + { + %popup = ETPolyObjectContextPopup; + if( !isObject( %popup ) ) + %popup = new PopupMenu( ETPolyObjectContextPopup : ETContextPopup ) + { + superClass = "MenuBuilder"; + isPopup = "1"; + + item[ 8 ] = "-"; + item[ 9 ] = "Convert to ConvexShape" TAB "" TAB "EWorldEditor.convertSelectionToConvexShape();"; + }; + } + + %popup.object = %obj; + %haveObjectEntries = true; + } + + if( %haveObjectEntries ) + { + %popup.enableItem( 0, %obj.isNameChangeAllowed() && %obj.getName() !$= "MissionGroup" ); + %popup.enableItem( 1, %obj.getName() !$= "MissionGroup" ); + if( %haveLockAndHideEntries ) + { + %popup.checkItem( 4, %obj.locked ); + %popup.checkItem( 5, %obj.hidden ); + } + %popup.enableItem( 7, %this.isItemSelected( %itemId ) ); + } + + %popup.showPopup( Canvas ); +} + +function EditorTree::positionContextMenu( %this, %menu ) +{ + if( (getWord(%menu.position, 0) + getWord(%menu.extent, 0)) > getWord(EWorldEditor.extent, 0) ) + { + %posx = getWord(%menu.position, 0); + %offset = getWord(EWorldEditor.extent, 0) - (%posx + getWord(%menu.extent, 0)) - 5; + %posx += %offset; + %menu.position = %posx @ " " @ getWord(%menu.position, 1); + } +} + +function EditorTree::isValidDragTarget( %this, %id, %obj ) +{ + if( %obj.isMemberOfClass( "Path" ) ) + return EWorldEditor.areAllSelectedObjectsOfType( "Marker" ); + if( %obj.name $= "CameraBookmarks" ) + return EWorldEditor.areAllSelectedObjectsOfType( "CameraBookmark" ); + else + return ( %obj.getClassName() $= "SimGroup" ); +} + +function EditorTree::onBeginReparenting( %this ) +{ + if( isObject( %this.reparentUndoAction ) ) + %this.reparentUndoAction.delete(); + + %action = UndoActionReparentObjects::create( %this ); + + %this.reparentUndoAction = %action; +} + +function EditorTree::onReparent( %this, %obj, %oldParent, %newParent ) +{ + %this.reparentUndoAction.add( %obj, %oldParent, %newParent ); +} + +function EditorTree::onEndReparenting( %this ) +{ + %action = %this.reparentUndoAction; + %this.reparentUndoAction = ""; + + if( %action.numObjects > 0 ) + { + if( %action.numObjects == 1 ) + %action.actionName = "Reparent Object"; + else + %action.actionName = "Reparent Objects"; + + %action.addToManager( Editor.getUndoManager() ); + + EWorldEditor.syncGui(); + } + else + %action.delete(); +} + +function EditorTree::update( %this ) +{ + %this.buildVisibleTree( false ); +} + +//------------------------------------------------------------------------------ + +// Tooltip for TSStatic +function EditorTree::GetTooltipTSStatic( %this, %obj ) +{ + return "Shape: " @ %obj.shapeName; +} + +// Tooltip for ShapeBase +function EditorTree::GetTooltipShapeBase( %this, %obj ) +{ + return "Datablock: " @ %obj.dataBlock; +} + +// Tooltip for StaticShape +function EditorTree::GetTooltipStaticShape( %this, %obj ) +{ + return "Datablock: " @ %obj.dataBlock; +} + +// Tooltip for Item +function EditorTree::GetTooltipItem( %this, %obj ) +{ + return "Datablock: " @ %obj.dataBlock; +} + +// Tooltip for RigidShape +function EditorTree::GetTooltipRigidShape( %this, %obj ) +{ + return "Datablock: " @ %obj.dataBlock; +} + +// Tooltip for Prefab +function EditorTree::GetTooltipPrefab( %this, %obj ) +{ + return "File: " @ %obj.filename; +} + +// Tooltip for GroundCover +function EditorTree::GetTooltipGroundCover( %this, %obj ) +{ + %text = "Material: " @ %obj.material; + for(%i=0; %i<8; %i++) + { + if(%obj.probability[%i] > 0 && %obj.shapeFilename[%i] !$= "") + { + %text = %text NL "Shape " @ %i @ ": " @ %obj.shapeFilename[%i]; + } + } + return %text; +} + +// Tooltip for SFXEmitter +function EditorTree::GetTooltipSFXEmitter( %this, %obj ) +{ + if(%obj.fileName $= "") + return "Track: " @ %obj.track; + else + return "File: " @ %obj.fileName; +} + +// Tooltip for ParticleEmitterNode +function EditorTree::GetTooltipParticleEmitterNode( %this, %obj ) +{ + %text = "Datablock: " @ %obj.dataBlock; + %text = %text NL "Emitter: " @ %obj.emitter; + return %text; +} + +// Tooltip for WorldEditorSelection +function EditorTree::GetTooltipWorldEditorSelection( %this, %obj ) +{ + %text = "Objects: " @ %obj.getCount(); + + if( !%obj.getCanSave() ) + %text = %text NL "Persistent: No"; + else + %text = %text NL "Persistent: Yes"; + + return %text; +} + +//------------------------------------------------------------------------------ + +function EditorTreeTabBook::onTabSelected( %this ) +{ + if( EditorTreeTabBook.getSelectedPage() == 0) + { + EWTreeWindow-->DeleteSelection.visible = true; + EWTreeWindow-->LockSelection.visible = true; + EWTreeWindow-->AddSimGroup.visible = true; + } + else + { + EWTreeWindow-->DeleteSelection.visible = false; + EWTreeWindow-->LockSelection.visible = false; + EWTreeWindow-->AddSimGroup.visible = false; + } +} + +//------------------------------------------------------------------------------ + +function Editor::open(%this) +{ + // prevent the mission editor from opening while the GuiEditor is open. + if(Canvas.getContent() == GuiEditorGui.getId()) + return; + + EditorGui.buildMenus(); + + if( !EditorGui.isInitialized ) + EditorGui.init(); + + %this.editorEnabled(); + Canvas.setContent(EditorGui); + EditorGui.syncCameraGui(); +} + +function Editor::close(%this, %gui) +{ + %this.editorDisabled(); + Canvas.setContent(%gui); + if(isObject(MessageHud)) + MessageHud.close(); + EditorGui.writeCameraSettings(); + + EditorGui.onDestroyMenu(); +} + +function EditorGui::onDestroyMenu(%this) +{ + if( !isObject( %this.menuBar ) ) + return; + + // Destroy menus + while( %this.menuBar.getCount() != 0 ) + %this.menuBar.getObject( 0 ).delete(); + + %this.menuBar.removeFromCanvas(); + %this.menuBar.delete(); +} + +$RelightCallback = ""; + +function EditorLightingComplete() +{ + $lightingMission = false; + RelightStatus.visible = false; + + if ($RelightCallback !$= "") + { + eval($RelightCallback); + } + + $RelightCallback = ""; +} + +function updateEditorLightingProgress() +{ + RelightProgress.setValue(($SceneLighting::lightingProgress)); + if ($lightingMission) + $lightingProgressThread = schedule(1, 0, "updateEditorLightingProgress"); +} + +function Editor::lightScene(%this, %callback, %forceAlways) +{ + if ($lightingMission) + return; + + $lightingMission = true; + $RelightCallback = %callback; + RelightStatus.visible = true; + RelightProgress.setValue(0); + Canvas.repaint(); + lightScene("EditorLightingComplete", %forceAlways); + updateEditorLightingProgress(); +} + +//------------------------------------------------------------------------------ + +function EditorGui::handleEscape( %this ) +{ + %result = false; + if ( isObject( %this.currentEditor ) ) + %result = %this.currentEditor.handleEscape(); + + if ( !%result ) + { + Editor.close("PlayGui"); + } +} + +function EditTSCtrl::updateGizmoMode( %this, %mode ) +{ + // Called when the gizmo mode is changed from C++ + + if ( %mode $= "None" ) + EditorGuiToolbar->NoneModeBtn.performClick(); + else if ( %mode $= "Move" ) + EditorGuiToolbar->MoveModeBtn.performClick(); + else if ( %mode $= "Rotate" ) + EditorGuiToolbar->RotateModeBtn.performClick(); + else if ( %mode $= "Scale" ) + EditorGuiToolbar->ScaleModeBtn.performClick(); +} + +//------------------------------------------------------------------------------ + +function EWorldEditor::syncGui( %this ) +{ + %this.syncToolPalette(); + + EditorTree.update(); + Editor.getUndoManager().updateUndoMenu( EditorGui.menuBar-->EditMenu ); + EditorGuiStatusBar.setSelectionObjectsByCount( %this.getSelectionSize() ); + + EWTreeWindow-->LockSelection.setStateOn( %this.getSelectionLockCount() > 0 ); + + EWorldEditorToolbar-->boundingBoxColBtn.setStateOn( EWorldEditor.boundingBoxCollision ); + + if( EWorldEditor.objectsUseBoxCenter ) + { + EWorldEditorToolbar-->centerObject.setBitmap("tools/gui/images/menubar/bounds-center"); + objectCenterDropdown-->objectBoundsBtn.setStateOn( 1 ); + } + else + { + EWorldEditorToolbar-->centerObject.setBitmap("tools/gui/images/menubar/object-center"); + objectCenterDropdown-->objectBoxBtn.setStateOn( 1 ); + } + + if( GlobalGizmoProfile.getFieldValue(alignment) $= "Object" ) + { + EWorldEditorToolbar-->objectTransform.setBitmap("tools/gui/images/menubar/object-transform"); + objectTransformDropdown-->objectTransformBtn.setStateOn( 1 ); + + } + else + { + EWorldEditorToolbar-->objectTransform.setBitmap("tools/gui/images/menubar/world-transform"); + objectTransformDropdown-->worldTransformBtn.setStateOn( 1 ); + } + + EWorldEditorToolbar-->renderHandleBtn.setStateOn( EWorldEditor.renderObjHandle ); + EWorldEditorToolbar-->renderTextBtn.setStateOn( EWorldEditor.renderObjText ); + + SnapToBar-->objectSnapBtn.setStateOn( EWorldEditor.getSoftSnap() ); + EWorldEditorToolbar-->softSnapSizeTextEdit.setText( EWorldEditor.getSoftSnapSize() ); + ESnapOptions-->SnapSize.setText( EWorldEditor.getSoftSnapSize() ); + ESnapOptions-->GridSize.setText( EWorldEditor.getGridSize() ); + + ESnapOptions-->GridSnapButton.setStateOn( %this.getGridSnap() ); + SnapToBar-->objectGridSnapBtn.setStateOn( %this.getGridSnap() ); + ESnapOptions-->NoSnapButton.setStateOn( !%this.stickToGround && !%this.getSoftSnap() && !%this.getGridSnap() ); +} + +function EWorldEditor::syncToolPalette( %this ) +{ + switch$ ( GlobalGizmoProfile.mode ) + { + case "None": + EWorldEditorNoneModeBtn.performClick(); + case "Move": + EWorldEditorMoveModeBtn.performClick(); + case "Rotate": + EWorldEditorRotateModeBtn.performClick(); + case "Scale": + EWorldEditorScaleModeBtn.performClick(); + } +} + +function EWorldEditor::addSimGroup( %this, %groupCurrentSelection ) +{ + %activeSelection = %this.getActiveSelection(); + if ( %activeSelection.getObjectIndex( MissionGroup ) != -1 ) + { + MessageBoxOK( "Error", "Cannot add MissionGroup to a new SimGroup" ); + return; + } + + // Find our parent. + + %parent = MissionGroup; + if( !%groupCurrentSelection && isObject( %activeSelection ) && %activeSelection.getCount() > 0 ) + { + %firstSelectedObject = %activeSelection.getObject( 0 ); + if( %firstSelectedObject.isMemberOfClass( "SimGroup" ) ) + %parent = %firstSelectedObject; + else if( %firstSelectedObject.getId() != MissionGroup.getId() ) + %parent = %firstSelectedObject.parentGroup; + } + + // If we are about to do a group-selected as well, + // starting recording an undo compound. + + if( %groupCurrentSelection ) + Editor.getUndoManager().pushCompound( "Group Selected" ); + + // Create the SimGroup. + + %object = new SimGroup() + { + parentGroup = %parent; + }; + MECreateUndoAction::submit( %object ); + + // Put selected objects into the group, if requested. + + if( %groupCurrentSelection && isObject( %activeSelection ) ) + { + %undo = UndoActionReparentObjects::create( EditorTree ); + + %numObjects = %activeSelection.getCount(); + for( %i = 0; %i < %numObjects; %i ++ ) + { + %sel = %activeSelection.getObject( %i ); + %undo.add( %sel, %sel.parentGroup, %object ); + %object.add( %sel ); + } + + %undo.addToManager( Editor.getUndoManager() ); + } + + // Stop recording for group-selected. + + if( %groupCurrentSelection ) + Editor.getUndoManager().popCompound(); + + // When not grouping selection, make the newly created SimGroup the + // current selection. + + if( !%groupCurrentSelection ) + { + EWorldEditor.clearSelection(); + EWorldEditor.selectObject( %object ); + } + + // Refresh the Gui. + + %this.syncGui(); +} + +function EWorldEditor::toggleLockChildren( %this, %simGroup ) +{ + foreach( %child in %simGroup ) + { + if( %child.class $= "SimGroup" ) + { + %this.toggleHideChildren( %child ); + } + if( %child.isMemberOfClass( "SimGroup" ) ) + { + %this.toggleHideChildren( %child ); + %child.setLocked( !%child.locked ); + } + else + { + %child.setLocked( !%child.locked ); + } + } + + EWorldEditor.syncGui(); +} + +function EWorldEditor::toggleHideChildren( %this, %simGroup ) +{ + foreach( %child in %simGroup ) + { + if( %child.class $= "SimGroup" ) + { + %this.toggleHideChildren( %child ); + } + if( %child.isMemberOfClass( "SimGroup" ) ) + { + %this.toggleHideChildren( %child ); + %this.hideObject( %child, !%child.hidden ); + } + else + { + %this.hideObject( %child, !%child.hidden ); + } + } + + EWorldEditor.syncGui(); +} + +function EWorldEditor::convertSelectionToPolyhedralObjects( %this, %className ) +{ + %group = %this.getNewObjectGroup(); + %undoManager = Editor.getUndoManager(); + + %activeSelection = %this.getActiveSelection(); + while( %activeSelection.getCount() != 0 ) + { + %oldObject = %activeSelection.getObject( 0 ); + %newObject = %this.createPolyhedralObject( %className, %oldObject ); + if( isObject( %newObject ) ) + { + %undoManager.pushCompound( "Convert ConvexShape to " @ %className ); + %newObject.parentGroup = %oldObject.parentGroup; + MECreateUndoAction::submit( %newObject ); + MEDeleteUndoAction::submit( %oldObject ); + %undoManager.popCompound(); + } + } +} + +function EWorldEditor::convertSelectionToConvexShape( %this ) +{ + %group = %this.getNewObjectGroup(); + %undoManager = Editor.getUndoManager(); + + %activeSelection = %this.getActiveSelection(); + while( %activeSelection.getCount() != 0 ) + { + %oldObject = %activeSelection.getObject( 0 ); + %newObject = %this.createConvexShapeFrom( %oldObject ); + if( isObject( %newObject ) ) + { + %undoManager.pushCompound( "Convert " @ %oldObject.getClassName() @ " to ConvexShape" ); + %newObject.parentGroup = %oldObject.parentGroup; + MECreateUndoAction::submit( %newObject ); + MEDeleteUndoAction::submit( %oldObject ); + %undoManager.popCompound(); + } + } +} + +function EWorldEditor::getNewObjectGroup( %this ) +{ + return EWCreatorWindow.getNewObjectGroup(); +} + +function EWorldEditor::deleteMissionObject( %this, %object ) +{ + // Unselect in editor tree. + + %id = EditorTree.findItemByObjectId( %object ); + EditorTree.selectItem( %id, false ); + + // Delete object. + + MEDeleteUndoAction::submit( %object ); + EWorldEditor.isDirty = true; + EditorTree.buildVisibleTree( true ); +} + +function EWorldEditor::createGameObject( %this, %entity ) +{ + if(!isObject(GameObjectBuilder)) + { + new GuiControl(GameObjectBuilder, EditorGuiGroup) { + profile = "ToolsGuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "0 0"; + extent = "800 600"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + + new GuiWindowCtrl(GameObjectBuilderTargetWindow) { + profile = "ToolsGuiWindowProfile"; + horizSizing = "center"; + vertSizing = "center"; + position = "384 205"; + extent = "256 102"; + minExtent = "256 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + resizeWidth = "1"; + resizeHeight = "1"; + canMove = "1"; + canClose = "0"; + canMinimize = "0"; + canMaximize = "0"; + minSize = "50 50"; + text = "Create Object"; + + new GuiTextCtrl() { + profile = "GuiCenterTextProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "9 26"; + extent = "84 16"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = "Object Name:"; + }; + new GuiTextEditCtrl(GameObjectBuilderObjectName) { + class = ObjectBuilderGuiTextEditCtrl; + profile = "ToolsGuiTextEditProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "78 26"; + extent = "172 18"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + historySize = "0"; + }; + new GuiButtonCtrl(GameObjectBuilderOKButton) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "width"; + vertSizing = "bottom"; + position = "7 250"; + extent = "156 24"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + command = "EWorldEditor.buildGameObject();"; + helpTag = "0"; + text = "Create New"; + Accelerator = "return"; + }; + new GuiButtonCtrl(GameObjectBuilderCancelButton) { + profile = "ToolsGuiButtonProfile"; + horizSizing = "left"; + vertSizing = "bottom"; + position = "170 250"; + extent = "80 24"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + command = "Canvas.popDialog(GameObjectBuilder);"; + helpTag = "0"; + text = "Cancel"; + Accelerator = "escape"; + }; + }; + }; + + GameObjectBuilderTargetWindow.extent = getWord(GameObjectBuilderTargetWindow.extent, 0) SPC 88; + GameObjectBuilderOKButton.position = getWord(GameObjectBuilderOKButton.position, 0) SPC 57; + GameObjectBuilderCancelButton.position = getWord(GameObjectBuilderCancelButton.position, 0) SPC 57; + } + + GameObjectBuilderObjectName.text = ""; + GameObjectBuilder.selectedEntity = %entity; + + Canvas.pushDialog(GameObjectBuilder); +} + +function EWorldEditor::buildGameObject(%this) +{ + if(GameObjectBuilderObjectName.getText() $= "") + { + error("Attempted to make a new Game Object with no name!"); + Canvas.popDialog(GameObjectBuilder); + return; + } + + %path = EditorSettings.value( "WorldEditor/newGameObjectDir" ); + %className = GameObjectBuilderObjectName.getText(); + GameObjectBuilder.selectedEntity.class = %className; + Inspector.inspect(GameObjectBuilder.selectedEntity); + + %file = new FileObject(); + + if(%file.openForWrite(%path @ "\\" @ %className @ ".cs")) + { + %file.writeline("function " @ %className @ "::onAdd(%this)\n{\n\n}\n"); + %file.writeline("function " @ %className @ "::onRemove(%this)\n{\n\n}\n"); + + //todo, pre-write any event functions of interest + + %file.close(); + } + + //set up the paths + %tamlPath = %path @ "/" @ %className @ ".taml"; + %scriptPath = %path @ "/" @ %className @ ".cs"; + saveGameObject(%className, %tamlPath, %scriptPath); + + //reload it + execGameObjects(); + + //now, add the script file and a ref to the taml into our SGO manifest so we can readily spawn it later. + TamlWrite(GameObjectBuilder.selectedEntity, %tamlpath); + + GameObjectBuilder.selectedEntity = ""; + + Canvas.popDialog(GameObjectBuilder); +} + +function EWorldEditor::selectAllObjectsInSet( %this, %set, %deselect ) +{ + if( !isObject( %set ) ) + return; + + foreach( %obj in %set ) + { + if( %deselect ) + %this.unselectObject( %obj ); + else + %this.selectObject( %obj ); + } +} + +function toggleSnappingOptions( %var ) +{ + if( SnapToBar->objectSnapDownBtn.getValue() && SnapToBar->objectSnapBtn.getValue() ) + { + if( %var $= "terrain" ) + { + EWorldEditor.stickToGround = 1; + EWorldEditor.setSoftSnap(false); + ESnapOptionsTabBook.selectPage(0); + SnapToBar->objectSnapBtn.setStateOn(0); + } + else + { + // soft snapping + EWorldEditor.stickToGround = 0; + EWorldEditor.setSoftSnap(true); + ESnapOptionsTabBook.selectPage(1); + SnapToBar->objectSnapDownBtn.setStateOn(0); + } + } + else if( %var $= "terrain" && EWorldEditor.stickToGround == 0 ) + { + // Terrain Snapping + EWorldEditor.stickToGround = 1; + EWorldEditor.setSoftSnap(false); + ESnapOptionsTabBook.selectPage(0); + SnapToBar->objectSnapDownBtn.setStateOn(1); + SnapToBar->objectSnapBtn.setStateOn(0); + + } + else if( %var $= "soft" && EWorldEditor.getSoftSnap() == false ) + { + // Object Snapping + EWorldEditor.stickToGround = 0; + EWorldEditor.setSoftSnap(true); + ESnapOptionsTabBook.selectPage(1); + SnapToBar->objectSnapBtn.setStateOn(1); + SnapToBar->objectSnapDownBtn.setStateOn(0); + + } + else if( %var $= "grid" ) + { + EWorldEditor.setGridSnap( !EWorldEditor.getGridSnap() ); + } + else + { + // No snapping. + + EWorldEditor.stickToGround = false; + EWorldEditor.setGridSnap( false ); + EWorldEditor.setSoftSnap( false ); + + SnapToBar->objectSnapDownBtn.setStateOn(0); + SnapToBar->objectSnapBtn.setStateOn(0); + } + + EWorldEditor.syncGui(); +} + +function objectCenterDropdown::toggle() +{ + if ( objectCenterDropdown.visible ) + { + EWorldEditorToolbar-->centerObject.setStateOn(false); + objectCenterDropdownDecoy.setVisible(false); + objectCenterDropdownDecoy.setActive(false); + objectCenterDropdown.setVisible(false); + } + else + { + EWorldEditorToolbar-->centerObject.setStateOn(true); + objectCenterDropdown.setVisible(true); + objectCenterDropdownDecoy.setActive(true); + objectCenterDropdownDecoy.setVisible(true); + } +} + +function objectTransformDropdown::toggle() +{ + if ( objectTransformDropdown.visible ) + { + EWorldEditorToolbar-->objectTransform.setStateOn(false); + objectTransformDropdownDecoy.setVisible(false); + objectTransformDropdownDecoy.setActive(false); + objectTransformDropdown.setVisible(false); + } + else + { + EWorldEditorToolbar-->objectTransform.setStateOn(true); + objectTransformDropdown.setVisible(true); + objectTransformDropdownDecoy.setActive(true); + objectTransformDropdownDecoy.setVisible(true); + } +} + +function objectSnapDropdownDecoy::onMouseLeave() +{ + objectSnapDropdown.toggle(); +} + +function objectCenterDropdownDecoy::onMouseLeave() +{ + objectCenterDropdown.toggle(); +} + +function objectTransformDropdownDecoy::onMouseLeave() +{ + objectTransformDropdown.toggle(); +} + +//------------------------------------------------------------------------------ + +function EWAddSimGroupButton::onDefaultClick( %this ) +{ + EWorldEditor.addSimGroup(); +} + +function EWAddSimGroupButton::onCtrlClick( %this ) +{ + EWorldEditor.addSimGroup( true ); +} + +//------------------------------------------------------------------------------ + +function EWToolsToolbar::reset( %this ) +{ + %count = ToolsToolbarArray.getCount(); + for( %i = 0 ; %i < %count; %i++ ) + ToolsToolbarArray.getObject(%i).setVisible(true); + + %this.setExtent((29 + 4) * %count + 12, 33); + %this.isClosed = 0; + EWToolsToolbar.isDynamic = 0; + + EWToolsToolbarDecoy.setVisible(false); + EWToolsToolbarDecoy.setExtent((29 + 4) * %count + 4, 31); + + %this-->resizeArrow.setBitmap( "tools/gui/images/collapse-toolbar" ); +} + +function EWToolsToolbar::toggleSize( %this, %useDynamics ) +{ + // toggles the size of the tooltoolbar. also goes through + // and hides each control not currently selected. we hide the controls + // in a very neat, spiffy way + + if ( %this.isClosed == 0 ) + { + %image = "tools/gui/images/expand-toolbar"; + + for( %i = 0 ; %i < ToolsToolbarArray.getCount(); %i++ ) + { + if( ToolsToolbarArray.getObject(%i).getValue() != 1 ) + ToolsToolbarArray.getObject(%i).setVisible(false); + } + + %this.setExtent(43, 33); + %this.isClosed = 1; + + if(!%useDynamics) + { + EWToolsToolbarDecoy.setVisible(true); + EWToolsToolbar.isDynamic = 1; + } + + EWToolsToolbarDecoy.setExtent(35, 31); + } + else + { + %image = "tools/gui/images/collapse-toolbar"; + + %count = ToolsToolbarArray.getCount(); + for( %i = 0 ; %i < %count; %i++ ) + ToolsToolbarArray.getObject(%i).setVisible(true); + + %this.setExtent((29 + 4) * %count + 12, 33); + %this.isClosed = 0; + + if(!%useDynamics) + { + EWToolsToolbarDecoy.setVisible(false); + EWToolsToolbar.isDynamic = 0; + } + + EWToolsToolbarDecoy.setExtent((29 + 4) * %count + 4, 32); + } + + %this-->resizeArrow.setBitmap( %image ); + +} + +function EWToolsToolbarDecoy::onMouseEnter( %this ) +{ + EWToolsToolbar.toggleSize(true); +} + +function EWToolsToolbarDecoy::onMouseLeave( %this ) +{ + EWToolsToolbar.toggleSize(true); +} + +//------------------------------------------------------------------------------ + +function EditorGuiStatusBar::reset( %this ) +{ + EWorldEditorStatusBarInfo.clearInfo(); +} + +function EditorGuiStatusBar::getInfo( %this ) +{ + return EWorldEditorStatusBarInfo.getValue(); +} + +function EditorGuiStatusBar::setInfo( %this, %text ) +{ + EWorldEditorStatusBarInfo.setText(%text); +} + +function EditorGuiStatusBar::clearInfo( %this ) +{ + EWorldEditorStatusBarInfo.setText(""); +} + +function EditorGuiStatusBar::getSelection( %this ) +{ + return EWorldEditorStatusBarSelection.getValue(); +} + +function EditorGuiStatusBar::setSelection( %this, %text ) +{ + EWorldEditorStatusBarSelection.setText(%text); +} + +function EditorGuiStatusBar::setSelectionObjectsByCount( %this, %count ) +{ + %text = " objects selected"; + if(%count == 1) + %text = " object selected"; + + EWorldEditorStatusBarSelection.setText(%count @ %text); +} + +function EditorGuiStatusBar::clearSelection( %this ) +{ + EWorldEditorStatusBarSelection.setText(""); +} + +function EditorGuiStatusBar::getCamera( %this ) +{ + return EWorldEditorStatusBarCamera.getText(); +} + +function EditorGuiStatusBar::setCamera( %this, %text ) +{ + %id = EWorldEditorStatusBarCamera.findText( %text ); + if( %id != -1 ) + { + if ( EWorldEditorStatusBarCamera.getSelected() != %id ) + EWorldEditorStatusBarCamera.setSelected( %id, true ); + } +} + +function EWorldEditorStatusBarCamera::onWake( %this ) +{ + %this.add( "Standard Camera" ); + %this.add( "1st Person Camera" ); + %this.add( "3rd Person Camera" ); + %this.add( "Orbit Camera" ); + %this.add( "Top View" ); + %this.add( "Bottom View" ); + %this.add( "Left View" ); + %this.add( "Right View" ); + %this.add( "Front View" ); + %this.add( "Back View" ); + %this.add( "Isometric View" ); + %this.add( "Smooth Camera" ); + %this.add( "Smooth Rot Camera" ); +} + +function EWorldEditorStatusBarCamera::onSelect( %this, %id, %text ) +{ + switch$( %text ) + { + case "Top View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeTop ); + + case "Bottom View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeBottom ); + + case "Left View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeLeft ); + + case "Right View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeRight ); + + case "Front View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeFront ); + + case "Back View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeBack ); + + case "Isometric View": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypeIsometric ); + + case "Standard Camera": + commandToServer( 'SetEditorCameraStandard' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + + case "1st Person Camera": + commandToServer( 'SetEditorCameraPlayer' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + + case "3rd Person Camera": + commandToServer( 'SetEditorCameraPlayerThird' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + + case "Orbit Camera": + commandToServer( 'SetEditorOrbitCamera' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + + case "Smooth Camera": + commandToServer( 'SetEditorCameraNewton' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + + case "Smooth Rot Camera": + commandToServer( 'SetEditorCameraNewtonDamped' ); + EditorGui.setDisplayType( $EditTsCtrl::DisplayTypePerspective ); + } +} + +//------------------------------------------------------------------------------------ +// Each a gui slider bar is pushed on the editor gui, it maps itself with value +// located in its connected text control +//------------------------------------------------------------------------------------ +function softSnapSizeSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(EWorldEditorToolbar-->softSnapSizeTextEdit.getValue()); +} +function softSnapSizeSliderCtrlContainer::onSliderChanged(%this) +{ + EWorldEditor.setSoftSnapSize( %this-->slider.value ); + EWorldEditor.syncGui(); +} +//------------------------------------------------------------------------------------ + +function PaintBrushSizeSliderCtrlContainer::onWake(%this) +{ + %this-->slider.range = "1" SPC getWord(ETerrainEditor.maxBrushSize, 0); + %this-->slider.setValue(PaintBrushSizeTextEditContainer-->textEdit.getValue()); +} + +function PaintBrushPressureSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(PaintBrushPressureTextEditContainer-->textEdit.getValue() / 100); +} + +function PaintBrushSoftnessSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(PaintBrushSoftnessTextEditContainer-->textEdit.getValue() / 100); +} + +//------------------------------------------------------------------------------------ + +function TerrainBrushSizeSliderCtrlContainer::onWake(%this) +{ + %this-->slider.range = "1" SPC getWord(ETerrainEditor.maxBrushSize, 0); + %this-->slider.setValue(TerrainBrushSizeTextEditContainer-->textEdit.getValue()); +} + +function TerrainBrushPressureSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(TerrainBrushPressureTextEditContainer-->textEdit.getValue() / 100.0); +} + +function TerrainBrushSoftnessSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(TerrainBrushSoftnessTextEditContainer-->textEdit.getValue() / 100.0); +} + +function TerrainSetHeightSliderCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(TerrainSetHeightTextEditContainer-->textEdit.getValue()); +} +//------------------------------------------------------------------------------------ +function CameraSpeedDropdownCtrlContainer::onWake(%this) +{ + %this-->slider.setValue(CameraSpeedDropdownContainer-->textEdit.getText()); +} + +//------------------------------------------------------------------------------------ +// Callbacks to close the dropdown slider controls like the camera speed, +// that are marked with this class name. + +function EditorDropdownSliderContainer::onMouseDown(%this) +{ + Canvas.popDialog(%this); +} + +function EditorDropdownSliderContainer::onRightMouseDown(%this) +{ + Canvas.popDialog(%this); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/ManageSFXParametersWindow.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/ManageSFXParametersWindow.ed.cs new file mode 100644 index 000000000..5e1134d38 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/ManageSFXParametersWindow.ed.cs @@ -0,0 +1,847 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//============================================================================= +// Constants. +//============================================================================= + +/// File to save newly created SFXParameters in by default. +$SFX_PARAMETER_FILE = "scripts/client/audioData.cs"; + +$SFX_PARAMETER_CHANNELS[ 0 ] = "Volume"; +$SFX_PARAMETER_CHANNELS[ 1 ] = "Pitch"; +$SFX_PARAMETER_CHANNELS[ 2 ] = "Priority"; +$SFX_PARAMETER_CHANNELS[ 3 ] = "MinDistance"; +$SFX_PARAMETER_CHANNELS[ 4 ] = "MaxDistance"; +$SFX_PARAMETER_CHANNELS[ 5 ] = "ConeInsideAngle"; +$SFX_PARAMETER_CHANNELS[ 6 ] = "ConeOutsideAngle"; +$SFX_PARAMETER_CHANNELS[ 7 ] = "ConeOutsideVolume"; +$SFX_PARAMETER_CHANNELS[ 8 ] = "PositionX"; +$SFX_PARAMETER_CHANNELS[ 9 ] = "PositionY"; +$SFX_PARAMETER_CHANNELS[ 10 ] = "PositionZ"; +$SFX_PARAMETER_CHANNELS[ 11 ] = "RotationX"; +$SFX_PARAMETER_CHANNELS[ 12 ] = "RotationY"; +$SFX_PARAMETER_CHANNELS[ 13 ] = "RotationZ"; +$SFX_PARAMETER_CHANNELS[ 14 ] = "VelocityX"; +$SFX_PARAMETER_CHANNELS[ 15 ] = "VelocityY"; +$SFX_PARAMETER_CHANNELS[ 16 ] = "VelocityZ"; +$SFX_PARAMETER_CHANNELS[ 17 ] = "Cursor"; +$SFX_PARAMETER_CHANNELS[ 18 ] = "User0"; +$SFX_PARAMETER_CHANNELS[ 19 ] = "User1"; +$SFX_PARAMETER_CHANNELS[ 20 ] = "User2"; +$SFX_PARAMETER_CHANNELS[ 21 ] = "User3"; + +$SFX_PARAMETER_CHANNELS_COUNT = 22; + +/// Interval (in milliseconds) between GUI updates. Each update +/// syncs the displayed values to the actual parameter states. +$SFX_PARAMETERS_UPDATE_INTERVAL = 50; + +//============================================================================= +// EManageSFXParameters. +//============================================================================= + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::createNewParameter( %this, %name ) +{ + %parameter = new SFXParameter() + { + internalName = %name; + }; + + if( !isObject( %parameter ) ) + return; + + %parameter.setFilename( $SFX_PARAMETER_FILE ); + %this.persistenceMgr.setDirty( %parameter ); + %this.persistenceMgr.saveDirty(); + + %this.addParameter( %parameter ); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::showDeleteParameterDlg( %this, %parameter ) +{ + MessageBoxOkCancel( "Confirmation", + "Really delete '" @ %parameter.getInternalName() @ "'?" NL + "" NL + "The parameter will be removed from the file '" @ %parameter.getFileName() @ "'.", + %this @ ".deleteParameter( " @ %parameter @ " );" + ); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::deleteParameter( %this, %parameter ) +{ + %this.removeParameter( %parameter ); + if( %parameter.getFilename() !$= "" ) + %this.persistenceMgr.removeObjectFromFile( %parameter ); + + %parameter.delete(); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::saveParameter( %this, %parameter ) +{ + if( %parameter.getFilename() !$= "" ) + { + if( !%this.persistenceMgr.isDirty( %parameter ) ) + %this.persistenceMgr.setDirty( %parameter ); + + %this.persistenceMgr.saveDirty(); + } +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::onWake( %this ) +{ + // If the parameter list is empty, add all SFXParameters in the + // SFXParameterGroup to the list. + + if( %this-->SFXParametersStack.getCount() == 0 ) + %this.initList(); + + if( !isObject( %this.persistenceMgr ) ) + %this.persistenceMgr = new PersistenceManager(); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::onVisible( %this, %value ) +{ + if( %value ) + { + // Schedule an update. + + %this.schedule( %SFX_PARAMETERS_UPDATE_INTERVAL, "update" ); + } +} + +//----------------------------------------------------------------------------- + +/// Populate the parameter list with the currently defined SFXParameters. +function EManageSFXParameters::initList( %this, %filter ) +{ + // Clear the current lists. + + %this-->SFXParametersStack.clear(); + + // Add each SFXParameter in SFXParameterGroup. + + foreach( %obj in SFXParameterGroup ) + { + if( !isMemberOfClass( %obj.getClassName(), "SFXParameter" ) ) + continue; + + // If we have a filter, search for it in the parameter's + // categories. + + %matchesFilter = true; + if( %filter !$= "" ) + { + %matchesFilter = false; + + for( %idx = 0; %obj.categories[ %idx ] !$= ""; %idx ++ ) + { + if( %obj.categories[ %idx ] $= %filter ) + { + %matchesFilter = true; + break; + } + } + } + + if( %matchesFilter ) + %this.addParameter( %obj ); + } + + // Init the filters. + + %this.initFilterList( %filter ); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::initFilterList( %this, %selectFilter ) +{ + %filterList = %this-->SFXParameterFilter; + %filterList.clear(); + %filterList.add( "", 0 ); + + foreach( %obj in SFXParameterGroup ) + { + if( !isMemberOfClass( %obj.getClassName(), "SFXParameter" ) ) + continue; + + for( %idx = 0; %obj.categories[ %idx ] !$= ""; %idx ++ ) + { + %category = %obj.categories[ %idx ]; + if( %filterList.findText( %category ) == -1 ) + %filterList.add( %category, %filterList.size() ); + } + } + + // Sort the filters. + + %filterList.sort(); + %filterList.setSelected( %filterList.findText( %selectFilter ), false ); +} + +//----------------------------------------------------------------------------- + +/// Parse the categories for the parameter from the given comma-separated list. +function EManageSFXParameters::updateParameterCategories( %this, %parameter, %list ) +{ + %this.persistenceMgr.setDirty( %parameter ); + + // Parse the list. + + %len = strlen( %list ); + %pos = 0; + + %idx = 0; + while( %pos < %len ) + { + %startPos = %pos; + %pos = strchrpos( %list, ",", %pos ); + if( %pos == -1 ) + %pos = %len; + + if( %pos > %startPos ) + { + %category = getSubStr( %list, %startPos, %pos - %startPos ); + %category = trim( %category ); + %parameter.categories[ %idx ] = %category; + %idx ++; + } + + %pos ++; + } + + // Clear out excess categories existing from before. + + while( %parameter.categories[ %idx ] !$= "" ) + { + %parameter.categories[ %idx ] = ""; + %this.persistenceMgr.removeField( %parameter, "categories" @ %idx ); + %idx ++; + } + + // Save the parameter. + + %this.saveParameter( %parameter ); + + // Re-initialize the filter list. + + %this.initFilterList( %this-->SFXParameterFilter.getText() ); +} + +//----------------------------------------------------------------------------- + +/// Add a new SFXParameter to the list. +function EManageSFXParameters::addParameter( %this, %parameter ) +{ + %ctrl = new GuiRolloutCtrl() { + Margin = "0 0 0 0"; + DefaultHeight = "40"; + Expanded = "1"; + ClickCollapse = "1"; + HideHeader = "0"; + isContainer = "1"; + Profile = "GuiRolloutProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 0"; + Extent = "421 114"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + caption = %parameter.getInternalName(); + + new GuiControl() { + isContainer = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "0 17"; + Extent = "421 94"; + MinExtent = "421 94"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + + new GuiTextCtrl() { + text = "Value"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 4"; + Extent = "27 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Channel"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 27"; + Extent = "45 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Comment"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 50"; + Extent = "47 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Tags"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "7 73"; + Extent = "25 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Min"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "205 27"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Max"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "271 27"; + Extent = "21 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "Initial"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiAutoSizeTextProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "340 27"; + Extent = "24 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + canSaveDynamicFields = "0"; + }; + new GuiSliderCtrl() { + range = "0 1"; + ticks = "0"; + snap = "0"; + value = "0.5"; + isContainer = "0"; + Profile = "ToolsGuiSliderProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "65 5"; + Extent = "263 15"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "valueSlider"; + canSaveDynamicFields = "0"; + command = %parameter @ ".value = $thisControl.getValue();"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "336 4"; + Extent = "39 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "valueField"; + canSaveDynamicFields = "0"; + altCommand = %parameter @ ".value = $thisControl.getValue();"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/reset-icon"; + autoFit = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "381 4"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "resetButton"; + canSaveDynamicFields = "0"; + command = %parameter @ ".reset();"; + }; + new GuiBitmapButtonCtrl() { + bitmap = "tools/gui/images/delete"; + autoFit = "0"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "398 4"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "deleteButton"; + canSaveDynamicFields = "0"; + command = "EManageSFXParameters.showDeleteParameterDlg( " @ %parameter @ ");"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "65 26"; + Extent = "135 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "channelDropdown"; + canSaveDynamicFields = "0"; + command = %parameter @ ".channel = $ThisControl.getText(); EManageSFXParameters.saveParameter( " @ %parameter @ ");"; //RDTODO: update range + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "65 50"; + Extent = "350 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "descriptionField"; + canSaveDynamicFields = "0"; + altCommand = %parameter @ ".description = $ThisControl.getText(); EManageSFXParameters.saveParameter( " @ %parameter @ ");"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "65 73"; + Extent = "230 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "tagsField"; + canSaveDynamicFields = "0"; + altCommand = "EManageSFXParameters.updateParameterCategories( " @ %parameter @ ", $ThisControl.getText() );"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "372 27"; + Extent = "43 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "defaultField"; + canSaveDynamicFields = "0"; + command = %parameter @ ".defaultValue = $ThisControl.getValue(); EManageSFXParameters.saveParameter( " @ %parameter @ ");"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "297 27"; + Extent = "39 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "rangeMaxField"; + canSaveDynamicFields = "0"; + altCommand = %parameter @ ".range = " @ %parameter @ ".range.x SPC $ThisControl.getValue(); $ThisControl.parentGroup-->valueSlider.range = " @ %parameter @ ".range; EManageSFXParameters.saveParameter( " @ %parameter @ ");"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + password = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + passwordMask = "•"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiTextEditProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "229 27"; + Extent = "39 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "rangeMinField"; + canSaveDynamicFields = "0"; + altCommand = %parameter @ ".range = $ThisControl.getValue() SPC " @ %parameter @ ".range.y; $ThisControl.parentGroup-->valueSlider.range = " @ %parameter @ ".range; EManageSFXParameters.saveParameter( " @ %parameter @ ");"; + }; + new GuiCheckBoxCtrl() { + useInactiveState = "0"; + text = "Local"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "302 73"; + Extent = "45 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "localCheckbox"; + canSaveDynamicFields = "0"; + }; + new GuiPopUpMenuCtrl() { + maxPopupHeight = "200"; + sbUsesNAColor = "0"; + reverseTextList = "0"; + bitmapBounds = "16 16"; + maxLength = "1024"; + Margin = "0 0 0 0"; + Padding = "0 0 0 0"; + AnchorTop = "1"; + AnchorBottom = "0"; + AnchorLeft = "1"; + AnchorRight = "0"; + isContainer = "0"; + Profile = "ToolsGuiPopUpMenuProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "349 73"; + Extent = "64 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + internalName = "sourceDropdown"; + canSaveDynamicFields = "0"; + }; + }; + }; + + %ctrl.sfxParameter = %parameter; + + // Deactivate the per-source controls for now as these are not + // yet implemented in SFX. + + %ctrl-->localCheckbox.setActive( false ); + %ctrl-->sourceDropdown.setActive( false ); + + // Set the fields to reflect the parameter's current settings. + + %ctrl-->valueField.setValue( %paramter.value ); + %ctrl-->rangeMinField.setText( %parameter.range.x ); + %ctrl-->rangeMaxField.setText( %parameter.range.y ); + %ctrl-->defaultField.setValue( %parameter.defaultValue ); + %ctrl-->descriptionField.setText( %parameter.description ); + + %ctrl-->valueSlider.range = %parameter.range; + %ctrl-->valueSlider.setValue( %parameter.value ); + + // Set up the channels dropdown. + + %list = %ctrl-->channelDropdown; + for( %i = 0; %i < $SFX_PARAMETER_CHANNELS_COUNT; %i ++ ) + %list.add( $SFX_PARAMETER_CHANNELS[ %i ], %i ); + %list.sort(); + %list.setSelected( %list.findText( %parameter.channel ) ); + + %this-->SFXParametersStack.addGuiControl( %ctrl ); + + // Fill tagging field. + + %tags = ""; + %isFirst = true; + for( %i = 0; %parameter.categories[ %i ] !$= ""; %i ++ ) + { + if( !%isFirst ) + %tags = %tags @ ", "; + + %tags = %tags @ %parameter.categories[ %i ]; + + %isFirst = false; + } + + %ctrl-->tagsField.setText( %tags ); +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::removeParameter( %this, %parameter ) +{ + foreach( %ctrl in %this-->SFXParametersStack ) + { + if( %ctrl.sfxParameter == %parameter ) + { + %ctrl.delete(); + break; + } + } +} + +//----------------------------------------------------------------------------- + +function EManageSFXParameters::update( %this ) +{ + foreach( %ctrl in %this-->SFXParametersStack ) + { + // If either the value field or the slider are currently being + // edited, don't update the value in order to not interfere with + // user editing. + + if( %ctrl-->valueField.isFirstResponder() || %ctrl-->valueSlider.isThumbBeingDragged() ) + continue; + + %parameter = %ctrl.sfxParameter; + + %ctrl-->valueField.setValue( %parameter.value ); + %ctrl-->valueSlider.setValue( %parameter.value ); + } + + // If the control is still awake, schedule another + // update. + + if( %this.isVisible() ) + %this.schedule( $SFX_PARAMETERS_UPDATE_INTERVAL, "update" ); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/SelectObjectsWindow.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/SelectObjectsWindow.ed.cs new file mode 100644 index 000000000..2c436f74f --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/SelectObjectsWindow.ed.cs @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +singleton SimGroup( EWorldEditorSelectionFilters ); + + +//--------------------------------------------------------------------------------------------- + +function ESelectObjectsWindow::toggleVisibility( %this ) +{ + if( %this.isVisible() ) + %this.setVisible( false ); + else + %this.setVisible( true ); +} + +//--------------------------------------------------------------------------------------------- + +/// Function called by EObjectSelection::onSelectObjects to determine where +/// to start searching for objects. +function ESelectObjectsWindow::getRootGroup( %this ) +{ + return MissionGroup; +} + +//--------------------------------------------------------------------------------------------- + +/// Function called by EObjectSelection::initFilterList to retrieve the set of +/// filter objects. +function ESelectObjectsWindow::getFilterSet( %this ) +{ + return EWorldEditorSelectionFilters; +} + +//--------------------------------------------------------------------------------------------- + +/// Method called by EObjectSelection::initClassList to determine if the given +/// class should be included in the class list. +function ESelectObjectsWindow::includeClass( %this, %className ) +{ + if( isMemberOfClass( %className, "SceneObject" ) + || %className $= "SimGroup" + || %className $= "LevelInfo" ) // Derived directly from NetObject. + return true; + + return false; +} + +//--------------------------------------------------------------------------------------------- + +/// Method called by the EObjectSelection machinery when an object has been +/// matched against the given criteria. +function ESelectObjectsWindow::selectObject( %this, %object, %val ) +{ + if( %this.selectionSet ) + { + if( %val ) + %this.selectionSet.add( %object ); + else + %this.selectionSet.remove( %object ); + } + else + { + if( %val ) + EWorldEditor.selectObject( %object ); + else + EWorldEditor.unselectObject( %object ); + } +} + +//--------------------------------------------------------------------------------------------- + +function ESelectObjectsWindow::clearSelection( %this ) +{ + if( %this.selectionSet ) + %this.selectionSet.clear(); + else + EWorldEditor.clearSelection(); +} + +//============================================================================================= +// Events. +//============================================================================================= + +//--------------------------------------------------------------------------------------------- + +function ESelectObjectsWindow::onWake( %this ) +{ + if( !%this.isInitialized ) + { + %this.init(); + %this.isInitialized = true; + } + + // Re-initialize the group list on each wake. + + %this.initGroupList(); +} + +//--------------------------------------------------------------------------------------------- + +function ESelectObjectsWindow::onSelectObjects( %this, %val, %reuseExistingSet ) +{ + // See if we should create an independent selection set. + + if( %this-->createSelectionSet.isStateOn() ) + { + %name = %this-->selectionSetName.getText(); + + // See if we should create or re-use a set. + + if( isObject( %name ) ) + { + if( !%name.isMemberOfClass( "WorldEditorSelection" ) ) + { + MessageBoxOk( "Error", + "An object called '" @ %name @ "' already exists and is not a selection." NL + "" NL + "Please choose a different name." ); + return; + } + else if( !%reuseExistingSet ) + { + MessageBoxYesNo( "Question", + "A selection called '" @ %name @ "' already exists. Modify the existing selection?", + %this @ ".onSelectObjects( " @ %val @ ", true );" ); + return; + } + else + %sel = %name; + } + else + { + // Make sure the name is valid. + if( !Editor::validateObjectName( %name, false ) ) + return; + + // Create a new selection set. + eval( "%sel = new WorldEditorSelection( " @ %name @ " ) { parentGroup = Selections; canSave = true; };" ); + if( !isObject( %sel ) ) + { + MessageBoxOk( "Error", + "Could not create the selection set. Please look at the console.log for details." ); + return; + } + } + + %this.selectionSet = %sel; + } + else + %this.selectionSet = ""; + + Parent::onSelectObjects( %this, %val ); + + // Refresh editor tree just in case. + + EditorTree.buildVisibleTree(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/cameraBookmarks.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/cameraBookmarks.ed.cs new file mode 100644 index 000000000..59a08803d --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/cameraBookmarks.ed.cs @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// CameraBookmark class methods. Located here so they won't fire without +// the tools in place. + +function CameraBookmark::onAdd( %this ) +{ +} + +function CameraBookmark::onRemove( %this ) +{ + if( isObject(EditorCameraBookmarks) ) + { + %pos = CameraBookmarks.getObjectIndex( %this ); + if( %pos != -1 ) + { + EditorCameraBookmarks.deleteItem( %pos ); + EManageBookmarks.deleteBookmark( %this, %pos ); + } + } +} + +function CameraBookmark::onGroupAdd( %this ) +{ + // If we're added to the CameraBookmarks group, then also add us + // to the menu and Manage Bookmarks window. + if( isObject(CameraBookmarks) ) + { + %pos = CameraBookmarks.getObjectIndex( %this ); + if( %pos != -1 ) + { + EditorCameraBookmarks.addItem( %pos, %this.internalName ); + EManageBookmarks.addBookmark( %this, %pos ); + } + } +} + +function CameraBookmark::onGroupRemove( %this ) +{ + // If we're part of the CameraBookmarks group, then also remove us from + // the menu and Manage Bookmarks window. + if( isObject(CameraBookmarks) ) + { + %pos = CameraBookmarks.getObjectIndex( %this ); + if( %pos != -1 ) + { + EditorCameraBookmarks.deleteItem( %pos ); + EManageBookmarks.deleteBookmark( %this, %pos ); + } + } +} + +function CameraBookmark::onInspectPostApply( %this ) +{ + EditorCameraBookmarks.rebuildBookmarks(); +} + +//----------------------------------------------------------------------------- + +function EditorCameraBookmarksMenu::onAdd( %this ) +{ + if(! isObject(%this.canvas)) + %this.canvas = Canvas; + + // Add any existing bookmarks + %this.rebuildBookmarks(); +} + +function EditorCameraBookmarksMenu::addItem( %this, %pos, %name ) +{ + if( %this.NoneItem == true ) + { + %this.NoneItem = false; + %this.removeItem( 0 ); + } + + %accel = ""; + %this.insertItem(%pos, %name !$= "-" ? %name : "", %accel); +} + +function EditorCameraBookmarksMenu::deleteItem( %this, %pos ) +{ + %this.removeItem( %pos ); + if( %this.getItemCount() == 0 && %this.NoneItem != true ) + { + %this.addItem( 0, "None" ); + %this.enableItem( 0, false ); + %this.NoneItem = true; + } +} + +function EditorCameraBookmarksMenu::onSelectItem( %this, %pos, %text ) +{ + if( %pos >= 0 && %pos < CameraBookmarks.getCount() ) + { + %mark = CameraBookmarks.getObject( %pos ); + EditorGui.jumpToBookmark( %mark.internalName ); + return true; + } + + return false; +} + +function EditorCameraBookmarksMenu::rebuildBookmarks( %this ) +{ + // Delete all current items + while( %this.getItemCount() > 0) + { + %this.removeItem( 0 ); + } + + // Add back in all of the bookmarks + if( isObject(CameraBookmarks) && CameraBookmarks.getCount() > 0 ) + { + for( %i=0; %i<CameraBookmarks.getCount(); %i++ ) + { + %mark = CameraBookmarks.getObject( %i ); + %this.addItem( %i, %mark.internalName ); + } + %this.NoneItem = false; + } + else + { + %this.addItem( 0, "None" ); + %this.enableItem( 0, false ); + %this.NoneItem = true; + } +} + +//----------------------------------------------------------------------------- + +function ManageBookmarksContainer::onOK( %this ) +{ + %name = EAddBookmarkWindowName.getText(); + EAddBookmarkWindowName.clearFirstResponder(); + + if( %name $= "" ) + { + // look for a NewCamera name to grab + for(%i = 0; ; %i++){ + %name = "NewCamera_" @ %i; + if( !CameraBookmarks.findObjectByInternalName(%name) ){ + break; + } + } + } + + // Check if the new bookmark name already exists + if( isObject(CameraBookmarks) && CameraBookmarks.findObjectByInternalName(%name) ) + { + %userName = %name; + for(%i = 0; ; %i++){ + %name = %userName @ "_" @ %i; + if( !CameraBookmarks.findObjectByInternalName(%name) ){ + break; + } + } + } + + EditorGui.addCameraBookmark( %name ); + EAddBookmarkWindowName.text = ""; + //%this.CloseWindow(); +} + +function EAddBookmarkWindowName::onReturn( %this ) +{ + // Same as clicking the Create Bookmark button + ManageBookmarksContainer.onOK(); +} + +//----------------------------------------------------------------------------- + +function EManageBookmarks::hideDialog( %this ) +{ + %this.setVisible(false); +} + +function EManageBookmarks::ToggleVisibility( %this ) +{ + if ( %this.visible ) + { + %this.setVisible(false); + EWorldEditor.EManageBookmarksDisplayed = false; + } + else + { + %this.setVisible(true); + %this.selectWindow(); + %this.setCollapseGroup(false); + EWorldEditor.EManageBookmarksDisplayed = true; + } +} + +function EManageBookmarks::addBookmark( %this, %mark, %index ) +{ + %gui = new GuiControl() { + internalName = %mark.getInternalName(); + Enabled = "1"; + Profile = "ToolsGuiDefaultProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = "300 20"; + MinExtent = "78 20"; + Visible = "1"; + Bookmark = %mark; + + new GuiBitmapButtonCtrl() { + class = "EManageBookmarksGoToButton"; + bitmap = "tools/gui/images/camera-btn"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + position = "2 2"; + Extent = "17 17"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Go to bookmark"; + hovertime = "1000"; + internalName = "goToBookmark"; + canSaveDynamicFields = "0"; + }; + + new GuiTextEditCtrl() { + class = "EManageBookmarksTextEdit"; + internalName = "BookmarkName"; + profile="ToolsGuiTextEditProfile"; + HorizSizing = "width"; + VertSizing = "bottom"; + position = "22 2"; + Extent = "260 18"; + text = %mark.getInternalName(); + maxLength = "1024"; + AltCommand = ""; + }; + + new GuiBitmapButtonCtrl() { + class = "EManageBookmarksDeleteButton"; + bitmap = "tools/gui/images/delete"; + groupNum = "-1"; + buttonType = "PushButton"; + useMouseEvents = "0"; + isContainer = "0"; + Profile = "ToolsGuiButtonProfile"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = "284 3"; + Extent = "16 16"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Command = ""; + tooltipprofile = "ToolsGuiToolTipProfile"; + ToolTip = "Delete camera bookmark"; + hovertime = "1000"; + internalName = "deleteBookmark"; + canSaveDynamicFields = "0"; + }; + }; + + EManageBookmarks-->ManageBookmarksWindowStack.addGuiControl( %gui ); +} + +function EManageBookmarks::deleteBookmark( %this, %mark, %index ) +{ + %gui = EManageBookmarks-->ManageBookmarksWindowStack.findObjectByInternalName( %mark.getInternalName() ); + if( %gui != 0 ) + %gui.delete(); + else + warn("EManageBookmarks::deleteBookmark(): Could not find bookmark " @ %mark @ " at index " @ %index); +} + +function EManageBookmarksGoToButton::onClick( %this ) +{ + %mark = %this.getParent().Bookmark; + EditorGui.jumpToBookmark( %mark.getInternalName() ); +} + +function EManageBookmarksDeleteButton::onClick( %this ) +{ + %mark = %this.getParent().Bookmark; + EditorGui.schedule( 0, removeCameraBookmark, %mark.getInternalName() ); +} + +function EManageBookmarksTextEdit::onGainFirstResponder( %this ) +{ + if( %this.isActive() ) + { + %this.selectAllText(); + } +} + +function EManageBookmarksTextEdit::onReturn( %this ) +{ + %this.onValidate(); +} + +function EManageBookmarksTextEdit::onValidate( %this ) +{ + %mark = %this.getParent().Bookmark; + %oldname = %mark.getInternalName(); + %newname = %this.getText(); + + // If the new name is the same as the old, do nothing + if( %newname $= %oldname ) + return; + + // Make sure the new name doesn't conflict with a current bookmark + if( isObject(CameraBookmarks) && CameraBookmarks.findObjectByInternalName(%newname) ) + { + %id = %this.getId(); + %callback = %id @ ".setText(\"" @ %oldname @ "\"); " @ %id @ ".makeFirstResponder(true); " @ %id @ ".selectAllText();"; + MessageBoxOK("Create Bookmark", "You must provide a unique name for the new bookmark.", %callback); + return; + } + + // Rename the bookmark and update + %this.getParent().setInternalName( %newname ); + %mark.setInternalName( %newname ); + if( Inspector.getInspectObject() == %mark.getId() ) + { + Inspector.inspect( %mark ); + Inspector.apply(); + } + else + { + // User is not inspecting the bookmark, so manually + // update the menu. + %mark.onInspectPostApply(); + } + +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/cameraCommands.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/cameraCommands.ed.cs new file mode 100644 index 000000000..c842b77c1 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/cameraCommands.ed.cs @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Sync the Camera and the EditorGui +function clientCmdSyncEditorGui() +{ + if (isObject(EditorGui)) + EditorGui.syncCameraGui(); +} + +//---------------------------------------------------------------------------- +// Camera commands +//---------------------------------------------------------------------------- +function serverCmdTogglePathCamera(%client, %val) +{ + if(%val) + { + %control = %client.PathCamera; + } + else + { + %control = %client.camera; + } + %client.setControlObject(%control); + clientCmdSyncEditorGui(); +} +function serverCmdToggleCamera(%client) +{ + if (%client.getControlObject() == %client.player) + { + %client.camera.setVelocity("0 0 0"); + %control = %client.camera; + } + else + { + %client.player.setVelocity("0 0 0"); + %control = %client.player; + } + %client.setControlObject(%control); + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorCameraPlayer(%client) +{ + // Switch to Player Mode + %client.player.setVelocity("0 0 0"); + %client.setControlObject(%client.player); + ServerConnection.setFirstPerson(1); + $isFirstPersonVar = 1; + + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorCameraPlayerThird(%client) +{ + // Swith to Player Mode + %client.player.setVelocity("0 0 0"); + %client.setControlObject(%client.player); + ServerConnection.setFirstPerson(0); + $isFirstPersonVar = 0; + + clientCmdSyncEditorGui(); +} + +function serverCmdDropPlayerAtCamera(%client) +{ + // If the player is mounted to something (like a vehicle) drop that at the + // camera instead. The player will remain mounted. + %obj = %client.player.getObjectMount(); + if (!isObject(%obj)) + %obj = %client.player; + + %obj.setTransform(%client.camera.getTransform()); + %obj.setVelocity("0 0 0"); + + %client.setControlObject(%client.player); + clientCmdSyncEditorGui(); +} + +function serverCmdDropCameraAtPlayer(%client) +{ + %client.camera.setTransform(%client.player.getEyeTransform()); + %client.camera.setVelocity("0 0 0"); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdCycleCameraFlyType(%client) +{ + if(%client.camera.getMode() $= "Fly") + { + if(%client.camera.newtonMode == false) // Fly Camera + { + // Switch to Newton Fly Mode without rotation damping + %client.camera.newtonMode = "1"; + %client.camera.newtonRotation = "0"; + %client.camera.setVelocity("0 0 0"); + } + else if(%client.camera.newtonRotation == false) // Newton Camera without rotation damping + { + // Switch to Newton Fly Mode with damped rotation + %client.camera.newtonMode = "1"; + %client.camera.newtonRotation = "1"; + %client.camera.setAngularVelocity("0 0 0"); + } + else // Newton Camera with rotation damping + { + // Switch to Fly Mode + %client.camera.newtonMode = "0"; + %client.camera.newtonRotation = "0"; + } + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); + } +} + +function serverCmdSetEditorCameraStandard(%client) +{ + // Switch to Fly Mode + %client.camera.setFlyMode(); + %client.camera.newtonMode = "0"; + %client.camera.newtonRotation = "0"; + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorCameraNewton(%client) +{ + // Switch to Newton Fly Mode without rotation damping + %client.camera.setFlyMode(); + %client.camera.newtonMode = "1"; + %client.camera.newtonRotation = "0"; + %client.camera.setVelocity("0 0 0"); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorCameraNewtonDamped(%client) +{ + // Switch to Newton Fly Mode with damped rotation + %client.camera.setFlyMode(); + %client.camera.newtonMode = "1"; + %client.camera.newtonRotation = "1"; + %client.camera.setAngularVelocity("0 0 0"); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorOrbitCamera(%client) +{ + %client.camera.setEditOrbitMode(); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdSetEditorFlyCamera(%client) +{ + %client.camera.setFlyMode(); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} + +function serverCmdEditorOrbitCameraSelectChange(%client, %size, %center) +{ + if(%size > 0) + { + %client.camera.setValidEditOrbitPoint(true); + %client.camera.setEditOrbitPoint(%center); + } + else + { + %client.camera.setValidEditOrbitPoint(false); + } +} + +function serverCmdEditorCameraAutoFit(%client, %radius) +{ + %client.camera.autoFitRadius(%radius); + %client.setControlObject(%client.camera); + clientCmdSyncEditorGui(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.cs new file mode 100644 index 000000000..f281b653a --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/cursors.ed.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Editor Cursors +//------------------------------------------------------------------------------ + +new GuiCursor(EditorHandCursor) +{ + hotSpot = "7 0"; + bitmapName = "~/worldEditor/images/CUR_hand.png"; +}; + +new GuiCursor(EditorRotateCursor) +{ + hotSpot = "11 18"; + bitmapName = "~/worldEditor/images/CUR_rotate.png"; +}; + +new GuiCursor(EditorMoveCursor) +{ + hotSpot = "9 13"; + bitmapName = "~/worldEditor/images/CUR_grab.png"; +}; + +new GuiCursor(EditorArrowCursor) +{ + hotSpot = "0 0"; + bitmapName = "~/worldEditor/images/CUR_3darrow.png"; +}; + +new GuiCursor(EditorUpDownCursor) +{ + hotSpot = "5 10"; + bitmapName = "~/worldEditor/images/CUR_3dupdown"; +}; +new GuiCursor(EditorLeftRightCursor) +{ + hotSpot = "9 5"; + bitmapName = "~/worldEditor/images/CUR_3dleftright"; +}; + +new GuiCursor(EditorDiagRightCursor) +{ + hotSpot = "8 8"; + bitmapName = "~/worldEditor/images/CUR_3ddiagright"; +}; + +new GuiCursor(EditorDiagLeftCursor) +{ + hotSpot = "8 8"; + bitmapName = "~/worldEditor/images/CUR_3ddiagleft"; +}; + +new GuiControl(EmptyControl) +{ + profile = "ToolsGuiButtonProfile"; +}; + + diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.bind.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.bind.ed.cs new file mode 100644 index 000000000..0c5b78a73 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.bind.ed.cs @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Mission Editor Manager +new ActionMap(EditorMap); + +function mouseWheelScroll( %val ) +{ + //$Camera::speedCurveTime += $Camera::scrollStepSize * ( (%val>0.0) ? 1 : -1 ); + //$Camera::speedCurveTime = mClamp( $Camera::speedCurveTime, 0.0, 1.0 ); + //calculateCameraSpeed(); + //EditorGui-->CameraSpeedSpinner.setText( $Camera::movementSpeed ); + + %rollAdj = getMouseAdjustAmount(%val); + %rollAdj = mClamp(%rollAdj, -mPi()+0.01, mPi()-0.01); + $mvRoll += %rollAdj; +} + +function editorYaw(%val) +{ + %yawAdj = getMouseAdjustAmount(%val); + + if(ServerConnection.isControlObjectRotDampedCamera() || EWorldEditor.isMiddleMouseDown()) + { + // Clamp and scale + %yawAdj = mClamp(%yawAdj, -m2Pi()+0.01, m2Pi()-0.01); + %yawAdj *= 0.5; + } + + if( EditorSettings.value( "Camera/invertXAxis" ) ) + %yawAdj *= -1; + + $mvYaw += %yawAdj; +} + +function editorPitch(%val) +{ + %pitchAdj = getMouseAdjustAmount(%val); + + if(ServerConnection.isControlObjectRotDampedCamera() || EWorldEditor.isMiddleMouseDown()) + { + // Clamp and scale + %pitchAdj = mClamp(%pitchAdj, -m2Pi()+0.01, m2Pi()-0.01); + %pitchAdj *= 0.5; + } + + if( EditorSettings.value( "Camera/invertYAxis" ) ) + %pitchAdj *= -1; + + $mvPitch += %pitchAdj; +} + +function editorWheelFadeScroll( %val ) +{ + EWorldEditor.fadeIconsDist += %val * 0.1; + if( EWorldEditor.fadeIconsDist < 0 ) + EWorldEditor.fadeIconsDist = 0; +} + +EditorMap.bind( mouse, xaxis, editorYaw ); +EditorMap.bind( mouse, yaxis, editorPitch ); +EditorMap.bind( mouse, zaxis, mouseWheelScroll ); + +EditorMap.bind( mouse, "alt zaxis", editorWheelFadeScroll ); diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.cs new file mode 100644 index 000000000..8545c9d67 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.ed.cs @@ -0,0 +1,201 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------ +// Hard coded images referenced from C++ code +//------------------------------------------------------------------------------ + +// editor/SelectHandle.png +// editor/DefaultHandle.png +// editor/LockedHandle.png + + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Mission Editor +//------------------------------------------------------------------------------ + +function Editor::create() +{ + // Not much to do here, build it and they will come... + // Only one thing... the editor is a gui control which + // expect the Canvas to exist, so it must be constructed + // before the editor. + new EditManager(Editor) + { + profile = "GuiContentProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + open = false; + }; +} + +function Editor::getUndoManager(%this) +{ + if ( !isObject( %this.undoManager ) ) + { + /// This is the global undo manager used by all + /// of the mission editor sub-editors. + %this.undoManager = new UndoManager( EUndoManager ) + { + numLevels = 200; + }; + } + return %this.undoManager; +} + +function Editor::setUndoManager(%this, %undoMgr) +{ + %this.undoManager = %undoMgr; +} + +function Editor::onAdd(%this) +{ + // Ignore Replicated fxStatic Instances. + EWorldEditor.ignoreObjClass("fxShapeReplicatedStatic"); +} + +function Editor::checkActiveLoadDone() +{ + if(isObject(EditorGui) && EditorGui.loadingMission) + { + Canvas.setContent(EditorGui); + EditorGui.loadingMission = false; + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +function toggleEditor(%make) +{ + if (%make) + { + %timerId = startPrecisionTimer(); + + if( GuiEditorIsActive() ) + toggleGuiEditor(1); + + if( !$missionRunning ) + { + // Flag saying, when level is chosen, launch it with the editor open. + ChooseLevelDlg.launchInEditor = true; + Canvas.pushDialog( ChooseLevelDlg ); + } + else + { + pushInstantGroup(); + + if ( !isObject( Editor ) ) + { + Editor::create(); + MissionCleanup.add( Editor ); + MissionCleanup.add( Editor.getUndoManager() ); + } + + if( EditorIsActive() ) + { + if (theLevelInfo.type $= "DemoScene") + { + commandToServer('dropPlayerAtCamera'); + Editor.close("SceneGui"); + } + else + { + Editor.close("PlayGui"); + } + } + else + { + canvas.pushDialog( EditorLoadingGui ); + canvas.repaint(); + + Editor.open(); + + // Cancel the scheduled event to prevent + // the level from cycling after it's duration + // has elapsed. + cancel($Game::Schedule); + + if (theLevelInfo.type $= "DemoScene") + commandToServer('dropCameraAtPlayer', true); + + canvas.popDialog(EditorLoadingGui); + } + + popInstantGroup(); + } + + %elapsed = stopPrecisionTimer( %timerId ); + warn( "Time spent in toggleEditor() : " @ %elapsed / 1000.0 @ " s" ); + } +} + +//------------------------------------------------------------------------------ +// The editor action maps are defined in editor.bind.cs +GlobalActionMap.bind(keyboard, "f11", toggleEditor); + + +// The scenario: +// The editor is open and the user closes the level by any way other than +// the file menu ( exit level ), eg. typing disconnect() in the console. +// +// The problem: +// Editor::close() is not called in this scenario which means onEditorDisable +// is not called on objects which hook into it and also gEditingMission will no +// longer be valid. +// +// The solution: +// Override the stock disconnect() function which is in game scripts from here +// in tools so we avoid putting our code in there. +// +// Disclaimer: +// If you think of a better way to do this feel free. The thing which could +// be dangerous about this is that no one will ever realize this code overriding +// a fairly standard and core game script from a somewhat random location. +// If it 'did' have unforscene sideeffects who would ever find it? + +package EditorDisconnectOverride +{ + function disconnect() + { + if ( isObject( Editor ) && Editor.isEditorEnabled() ) + { + if (isObject( MainMenuGui )) + Editor.close("MainMenuGui"); + } + + Parent::disconnect(); + } +}; +activatePackage( EditorDisconnectOverride ); diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editor.keybinds.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.keybinds.cs new file mode 100644 index 000000000..92ac42d8c --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editor.keybinds.cs @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +if ( isObject( editorMoveMap ) ) + editorMoveMap.delete(); + +new ActionMap(editorMoveMap); + +//------------------------------------------------------------------------------ +// Non-remapable binds +//------------------------------------------------------------------------------ +editorMoveMap.bindCmd(keyboard, "escape", "", "Canvas.pushDialog(PauseMenu);"); + +//------------------------------------------------------------------------------ +// Movement Keys +//------------------------------------------------------------------------------ +editorMoveMap.bind( keyboard, a, editorMoveleft ); +editorMoveMap.bind( keyboard, d, editorMoveright ); +editorMoveMap.bind( keyboard, left, editorMoveleft ); +editorMoveMap.bind( keyboard, right, editorMoveright ); + +editorMoveMap.bind( keyboard, w, editorMoveforward ); +editorMoveMap.bind( keyboard, s, editorMovebackward ); +editorMoveMap.bind( keyboard, up, editorMoveforward ); +editorMoveMap.bind( keyboard, down, editorMovebackward ); + +editorMoveMap.bind( keyboard, e, editorMoveup ); +editorMoveMap.bind( keyboard, c, editorMovedown ); + +editorMoveMap.bind( mouse, xaxis, editorYaw ); +editorMoveMap.bind( mouse, yaxis, editorPitch ); + +//------------------------------------------------------------------------------ +// Mouse Trigger +//------------------------------------------------------------------------------ +editorMoveMap.bind( mouse, button0, editorClick ); +editorMoveMap.bind( mouse, button1, editorRClick ); + +//------------------------------------------------------------------------------ +// Camera & View functions +//------------------------------------------------------------------------------ +editorMoveMap.bind(keyboard, "alt c", toggleCamera); + +//------------------------------------------------------------------------------ +// Helper Functions +//------------------------------------------------------------------------------ +editorMoveMap.bind(keyboard, "F8", dropCameraAtPlayer); +editorMoveMap.bind(keyboard, "F7", dropPlayerAtCamera); + +//------------------------------------------------------------------------------ +// Debugging Functions +//------------------------------------------------------------------------------ +GlobalActionMap.bind(keyboard, "ctrl F2", showMetrics); +GlobalActionMap.bind(keyboard, "ctrl F3", doProfile); + +//------------------------------------------------------------------------------ +// Misc. +//------------------------------------------------------------------------------ +GlobalActionMap.bind(keyboard, "tilde", toggleConsole); + +editorMoveMap.bind( mouse, "alt zaxis", editorWheelFadeScroll ); diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorInputCommands.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorInputCommands.cs new file mode 100644 index 000000000..aa6f8d8b6 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorInputCommands.cs @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Non-remapable binds +//------------------------------------------------------------------------------ +function escapeFromGame() +{ + disconnect(); +} + +//------------------------------------------------------------------------------ +// Movement Keys +//------------------------------------------------------------------------------ +function editorMoveleft(%val) +{ + $mvLeftAction = %val * $Camera::movementSpeed; +} + +function editorMoveright(%val) +{ + $mvRightAction = %val * $Camera::movementSpeed; +} + +function editorMoveforward(%val) +{ + $mvForwardAction = %val * $Camera::movementSpeed; +} + +function editorMovebackward(%val) +{ + $mvBackwardAction = %val * $Camera::movementSpeed; +} + +function editorMoveup(%val) +{ + %object = ServerConnection.getControlObject(); + + if(%object.isInNamespaceHierarchy("Camera")) + $mvUpAction = %val * $Camera::movementSpeed; +} + +function editorMovedown(%val) +{ + %object = ServerConnection.getControlObject(); + + if(%object.isInNamespaceHierarchy("Camera")) + $mvDownAction = %val * $Camera::movementSpeed; +} + +function getEditorMouseAdjustAmount(%val) +{ + return(%val * ($cameraFov / 90) * 0.01); +} + +function editorYaw(%val) +{ + %yawAdj = getEditorMouseAdjustAmount(%val); + if(ServerConnection.isControlObjectRotDampedCamera()) + { + // Clamp and scale + %yawAdj = mClamp(%yawAdj, -m2Pi()+0.01, m2Pi()-0.01); + %yawAdj *= 0.5; + } + + $mvYaw += %yawAdj; +} + +function editorPitch(%val) +{ + %pitchAdj = getEditorMouseAdjustAmount(%val); + if(ServerConnection.isControlObjectRotDampedCamera()) + { + // Clamp and scale + %pitchAdj = mClamp(%pitchAdj, -m2Pi()+0.01, m2Pi()-0.01); + %pitchAdj *= 0.5; + } + + $mvPitch += %pitchAdj; +} + +//------------------------------------------------------------------------------ +// Mouse Trigger +//------------------------------------------------------------------------------ +function editorClick(%val) +{ + $mvTriggerCount0++; +} + +function editorRClick(%val) +{ + $mvTriggerCount1++; +} + +//------------------------------------------------------------------------------ +// Camera & View functions +//------------------------------------------------------------------------------ +function toggleCamera(%val) +{ + if (%val) + commandToServer('ToggleCamera'); +} + +//------------------------------------------------------------------------------ +// Helper Functions +//------------------------------------------------------------------------------ +function dropCameraAtPlayer(%val) +{ + if (%val) + commandToServer('dropCameraAtPlayer'); +} + +function dropPlayerAtCamera(%val) +{ + if (%val) + commandToServer('DropPlayerAtCamera'); +} + +//------------------------------------------------------------------------------ +// Debugging Functions +//------------------------------------------------------------------------------ +function showMetrics(%val) +{ + if(%val) + { + if(!Canvas.isMember(FrameOverlayGui)) + metrics("fps gfx shadow sfx terrain groundcover forest net"); + else + metrics(""); + } +} + +//------------------------------------------------------------------------------ +// +// Start profiler by pressing ctrl f3 +// ctrl f3 - starts profile that will dump to console and file +// +function doProfile(%val) +{ + if (%val) + { + // key down -- start profile + echo("Starting profile session..."); + profilerReset(); + profilerEnable(true); + } + else + { + // key up -- finish off profile + echo("Ending profile session..."); + + profilerDumpToFile("profilerDumpToFile" @ getSimTime() @ ".txt"); + profilerEnable(false); + } +} + +function editorWheelFadeScroll( %val ) +{ + EWorldEditor.fadeIconsDist += %val * 0.1; + if( EWorldEditor.fadeIconsDist < 0 ) + EWorldEditor.fadeIconsDist = 0; +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorPlugin.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPlugin.ed.cs new file mode 100644 index 000000000..7bafa3838 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPlugin.ed.cs @@ -0,0 +1,203 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +/// +/// This is used to register editor extensions and tools. +/// +/// There are various callbacks you can overload to hook in your +/// own functionality without changing the core editor code. +/// +/// At the moment this is primarily for the World/Mission +/// Editor and the callbacks mostly make sense in that context. +/// +/// Example: +/// +/// %obj = new ScriptObject() +/// { +/// superclass = "EditorPlugin"; +/// class = "RoadEditor"; +/// }; +/// +/// EditorPlugin::register( %obj ); +/// +/// For an a full example see: tools/roadEditor/main.cs +/// or: tools/riverEditor/main.cs +/// or: tools/decalEditor/main.cs +/// + +/// It is not intended for the user to overload this method. +/// If you do make sure you call the parent. +function EditorPlugin::onAdd( %this ) +{ + EditorPluginSet.add( %this ); +} + + +/// Callback when the world editor is first started. It +/// is a good place to insert menus and menu items as well as +/// preparing guis. +function EditorPlugin::onWorldEditorStartup( %this ) +{ +} + +/// Callback when the world editor is about to be totally deleted. +/// At the time of this writing this occurs when the engine is shut down +/// and the editor had been initialized. +function EditorPlugin::onWorldEditorShutdown( %this ) +{ +} + +/// Callback right before the editor is opened. +function EditorPlugin::onEditorWake( %this ) +{ +} + +/// Callback right before the editor is closed. +function EditorPlugin::onEditorSleep( %this ) +{ +} + +/// Callback when the tool is 'activated' by the WorldEditor +/// Push Gui's, stuff like that +function EditorPlugin::onActivated( %this ) +{ + %this.isActivated = true; +} + +/// Callback when the tool is 'deactivated' / closed by the WorldEditor +/// Pop Gui's, stuff like that +function EditorPlugin::onDeactivated( %this ) +{ + %this.isActivated = false; +} + +/// Callback when tab is pressed. +/// Used by the WorldEditor to toggle between inspector/creator, for example. +function EditorPlugin::onToggleToolWindows( %this ) +{ +} + +/// Callback when the edit menu is clicked or prior to handling an accelerator +/// key event mapped to an edit menu item. +/// It is up to the active editor to determine if these actions are +/// appropriate in the current state. +function EditorPlugin::onEditMenuSelect( %this, %editMenu ) +{ + %editMenu.enableItem( 3, false ); // Cut + %editMenu.enableItem( 4, false ); // Copy + %editMenu.enableItem( 5, false ); // Paste + %editMenu.enableItem( 6, false ); // Delete + %editMenu.enableItem( 8, false ); // Deselect +} + +/// If this tool keeps track of changes that necessitate resaving the mission +/// return true in that case. +function EditorPlugin::isDirty( %this ) +{ + return false; +} + +/// This gives tools a chance to clear whatever internal variables keep track of changes +/// since the last save. +function EditorPlugin::clearDirty( %this ) +{ +} + +/// This gives tools chance to save data out when the mission is being saved. +/// This will only be called if the tool says it is dirty. +function EditorPlugin::onSaveMission( %this, %missionFile ) +{ +} + +/// Called when during mission cleanup to notify plugins. +function EditorPlugin::onExitMission( %this ) +{ +} + +/// Called on the active plugin when a SceneObject is selected. +/// +/// @param object The object being selected. +function EditorPlugin::onObjectSelected( %this, %object ) +{ +} + +/// Called on the active plugin when a SceneObject is deselected. +/// +/// @param object The object being deselected. +function EditorPlugin::onObjectDeselected( %this, %object ) +{ +} + +/// Called on the active plugin when the selection of SceneObjects is cleared. +function EditorPlugin::onSelectionCleared( %this ) +{ +} + +/// Callback when the the delete item of the edit menu is selected or its +/// accelerator is pressed. +function EditorPlugin::handleDelete( %this ) +{ + warn( "EditorPlugin::handleDelete( " @ %this.getName() @ " )" NL + "Was not implemented in child namespace, yet menu item was enabled." ); +} + +/// Callback when the the deselect item of the edit menu is selected or its +/// accelerator is pressed. +function EditorPlugin::handleDeselect( %this ) +{ + warn( "EditorPlugin::handleDeselect( " @ %this.getName() @ " )" NL + "Was not implemented in child namespace, yet menu item was enabled." ); +} + +/// Callback when the the cut item of the edit menu is selected or its +/// accelerator is pressed. +function EditorPlugin::handleCut( %this ) +{ + warn( "EditorPlugin::handleCut( " @ %this.getName() @ " )" NL + "Was not implemented in child namespace, yet menu item was enabled." ); +} + +/// Callback when the the copy item of the edit menu is selected or its +/// accelerator is pressed. +function EditorPlugin::handleCopy( %this ) +{ + warn( "EditorPlugin::handleCopy( " @ %this.getName() @ " )" NL + "Was not implemented in child namespace, yet menu item was enabled." ); +} + +/// Callback when the the paste item of the edit menu is selected or its +/// accelerator is pressed. +function EditorPlugin::handlePaste( %this ) +{ + warn( "EditorPlugin::handlePaste( " @ %this.getName() @ " )" NL + "Was not implemented in child namespace, yet menu item was enabled." ); +} + +/// Callback when the escape key is pressed. +/// Return true if this tool has handled the key event in a custom way. +/// If false is returned the WorldEditor default behavior is to return +/// to the ObjectEditor. +function EditorPlugin::handleEscape( %this ) +{ + return false; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs new file mode 100644 index 000000000..1704e06ad --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorPrefs.ed.cs @@ -0,0 +1,435 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +// Provide default values for all World Editor settings. These make use of +// the EditorSettings instance of the Settings class, as defined in the Tools +// package onStart(). + +EditorSettings.beginGroup( "WorldEditor", true ); +EditorSettings.setDefaultValue( "currentEditor", "WorldEditorInspectorPlugin" ); +EditorSettings.setDefaultValue( "dropType", "screenCenter" ); +EditorSettings.setDefaultValue( "undoLimit", "40" ); +EditorSettings.setDefaultValue( "forceLoadDAE", "0" ); +EditorSettings.setDefaultValue( "displayType", $EditTsCtrl::DisplayTypePerspective ); +EditorSettings.setDefaultValue( "orthoFOV", "50" ); +EditorSettings.setDefaultValue( "orthoShowGrid", "1" ); +EditorSettings.setDefaultValue( "currentEditor", "WorldEditorInspectorPlugin" ); +EditorSettings.setDefaultValue( "newLevelFile", "tools/levels/BlankRoom.mis" ); +EditorSettings.setDefaultValue( "newGameObjectDir", "scripts/server/gameObjects" ); + +if( isFile( "C:/Program Files/Torsion/Torsion.exe" ) ) + EditorSettings.setDefaultValue( "torsionPath", "C:/Program Files/Torsion/Torsion.exe" ); +else if( isFile( "C:/Program Files (x86)/Torsion/Torsion.exe" ) ) + EditorSettings.setDefaultValue( "torsionPath", "C:/Program Files (x86)/Torsion/Torsion.exe" ); +else + EditorSettings.setDefaultValue( "torsionPath", "" ); + +EditorSettings.beginGroup( "ObjectIcons" ); +EditorSettings.setDefaultValue( "fadeIcons", "1" ); +EditorSettings.setDefaultValue( "fadeIconsStartDist", "8" ); +EditorSettings.setDefaultValue( "fadeIconsEndDist", "20" ); +EditorSettings.setDefaultValue( "fadeIconsStartAlpha", "255" ); +EditorSettings.setDefaultValue( "fadeIconsEndAlpha", "0" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Grid" ); +EditorSettings.setDefaultValue( "gridSize", "1" ); +EditorSettings.setDefaultValue( "gridSnap", "0" ); +EditorSettings.setDefaultValue( "gridColor", "102 102 102 100" ); +EditorSettings.setDefaultValue( "gridOriginColor", "255 255 255 100" ); +EditorSettings.setDefaultValue( "gridMinorColor", "51 51 51 100" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Tools" ); +EditorSettings.setDefaultValue( "snapGround", "0" ); +EditorSettings.setDefaultValue( "snapSoft", "0" ); +EditorSettings.setDefaultValue( "snapSoftSize", "2.0" ); +EditorSettings.setDefaultValue( "boundingBoxCollision", "0" ); +EditorSettings.setDefaultValue( "objectsUseBoxCenter", "1" ); +EditorSettings.setDefaultValue( "dropAtScreenCenterScalar","1.0" ); +EditorSettings.setDefaultValue( "dropAtScreenCenterMax", "100.0" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Render" ); +EditorSettings.setDefaultValue( "renderObjHandle", "1" ); +EditorSettings.setDefaultValue( "renderObjText", "1" ); +EditorSettings.setDefaultValue( "renderPopupBackground", "1" ); +EditorSettings.setDefaultValue( "renderSelectionBox", "1" ); //<-- Does not currently render +EditorSettings.setDefaultValue( "showMousePopupInfo", "1" ); +//EditorSettings.setDefaultValue( "visibleDistanceScale", "1" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Color" ); +EditorSettings.setDefaultValue( "dragRectColor", "255 255 0 255" ); +EditorSettings.setDefaultValue( "objectTextColor", "255 255 255 255" ); +EditorSettings.setDefaultValue( "objMouseOverColor", "0 255 0 255" ); //<-- Currently ignored by editor (always white) +EditorSettings.setDefaultValue( "objMouseOverSelectColor", "0 0 255 255" ); //<-- Currently ignored by editor (always white) +EditorSettings.setDefaultValue( "objSelectColor", "255 0 0 255" ); //<-- Currently ignored by editor (always white) +EditorSettings.setDefaultValue( "popupBackgroundColor", "100 100 100 255" ); +EditorSettings.setDefaultValue( "popupTextColor", "255 255 0 255" ); +EditorSettings.setDefaultValue( "raceSelectColor", "0 0 100 100" ); //<-- What is this used for? +EditorSettings.setDefaultValue( "selectionBoxColor", "255 255 0 255" ); //<-- Does not currently render +EditorSettings.setDefaultValue( "uvEditorHandleColor", "1" ); //<-- Index into color popup +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Images" ); +EditorSettings.setDefaultValue( "defaultHandle", "tools/worldEditor/images/DefaultHandle" ); +EditorSettings.setDefaultValue( "lockedHandle", "tools/worldEditor/images/LockedHandle" ); +EditorSettings.setDefaultValue( "selectHandle", "tools/worldEditor/images/SelectHandle" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "Docs" ); +EditorSettings.setDefaultValue( "documentationLocal", "../../../Documentation/Official Documentation.html" ); +EditorSettings.setDefaultValue( "documentationReference", "../../../Documentation/Torque 3D - Script Manual.chm"); +EditorSettings.setDefaultValue( "documentationURL", "http://www.garagegames.com/products/torque-3d/documentation/user" ); +EditorSettings.setDefaultValue( "forumURL", "http://www.garagegames.com/products/torque-3d/forums" ); +EditorSettings.endGroup(); + +EditorSettings.endGroup(); // WorldEditor + +//------------------------------------- + +// After setting up the default value, this field should be altered immediately +// after successfully using such functionality such as Open... or Save As... +EditorSettings.beginGroup( "LevelInformation" ); +EditorSettings.setDefaultValue( "levelsDirectory", "levels" ); +EditorSettings.endGroup(); + +//------------------------------------- + +EditorSettings.beginGroup( "AxisGizmo", true ); + +EditorSettings.setDefaultValue( "axisGizmoMaxScreenLen", "100" ); //<-- What is this used for? +EditorSettings.setDefaultValue( "rotationSnap", "15" ); //<-- Not currently used +EditorSettings.setDefaultValue( "snapRotations", "0" ); //<-- Not currently used +EditorSettings.setDefaultValue( "mouseRotateScalar", "0.8" ); +EditorSettings.setDefaultValue( "mouseScaleScalar", "0.8" ); +EditorSettings.setDefaultValue( "renderWhenUsed", "0" ); +EditorSettings.setDefaultValue( "renderInfoText", "1" ); + +EditorSettings.beginGroup( "Grid" ); +EditorSettings.setDefaultValue( "gridColor", "255 255 255 20" ); +EditorSettings.setDefaultValue( "gridSize", "10 10 10" ); +EditorSettings.setDefaultValue( "snapToGrid", "0" ); //<-- Not currently used +EditorSettings.setDefaultValue( "renderPlane", "0" ); +EditorSettings.setDefaultValue( "renderPlaneHashes", "0" ); +EditorSettings.setDefaultValue( "planeDim", "500" ); +EditorSettings.endGroup(); + +EditorSettings.endGroup(); + +//------------------------------------- + +EditorSettings.beginGroup( "TerrainEditor", true ); + +EditorSettings.setDefaultValue( "currentAction", "raiseHeight" ); + +EditorSettings.beginGroup( "Brush" ); +EditorSettings.setDefaultValue( "maxBrushSize", "40 40" ); +EditorSettings.setDefaultValue( "brushSize", "1 1" ); +EditorSettings.setDefaultValue( "brushType", "box" ); +EditorSettings.setDefaultValue( "brushPressure", "1" ); +EditorSettings.setDefaultValue( "brushSoftness", "1" ); +EditorSettings.endGroup(); + +EditorSettings.beginGroup( "ActionValues" ); +EditorSettings.setDefaultValue( "adjustHeightVal", "10" ); +EditorSettings.setDefaultValue( "setHeightVal", "100" ); +EditorSettings.setDefaultValue( "scaleVal", "1" ); //<-- Tool not currently implemented +EditorSettings.setDefaultValue( "smoothFactor", "0.1" ); +EditorSettings.setDefaultValue( "noiseFactor", "1.0" ); +EditorSettings.setDefaultValue( "softSelectRadius", "50" ); +EditorSettings.setDefaultValue( "softSelectFilter", "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000" ); +EditorSettings.setDefaultValue( "softSelectDefaultFilter", "1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000" ); +EditorSettings.setDefaultValue( "slopeMinAngle", "0" ); +EditorSettings.setDefaultValue( "slopeMaxAngle", "90" ); +EditorSettings.endGroup(); + +EditorSettings.endGroup(); + +//------------------------------------- + +EditorSettings.beginGroup( "TerrainPainter", true ); +EditorSettings.endGroup(); + +//------------------------------------- + +//TODO: this doesn't belong here +function setDefault( %name, %value ) +{ + if( !isDefined( %name ) ) + eval( %name SPC "=" SPC "\"" @ %value @ "\";" ); +} + +setDefault( "$pref::WorldEditor::visibleDistanceScale", "1" ); // DAW: Keep this around for now as is used by EditTSCtrl + +// JCF: Couldn't some or all of these be exposed +// from WorldEditor::ConsoleInit via Con::AddVariable() +// and do away with this file? + +function EditorGui::readWorldEditorSettings(%this) +{ + EditorSettings.beginGroup( "WorldEditor", true ); + EWorldEditor.dropType = EditorSettings.value( "dropType" ); //$pref::WorldEditor::dropType; + EWorldEditor.undoLimit = EditorSettings.value( "undoLimit" ); //$pref::WorldEditor::undoLimit; + EWorldEditor.forceLoadDAE = EditorSettings.value( "forceLoadDAE" ); //$pref::WorldEditor::forceLoadDAE; + %this.currentDisplayType = EditorSettings.value( "displayType" ); + %this.currentOrthoFOV = EditorSettings.value( "orthoFOV" ); + EWorldEditor.renderOrthoGrid = EditorSettings.value( "orthoShowGrid" ); + %this.currentEditor = EditorSettings.value( "currentEditor" ); + %this.torsionPath = EditorSettings.value( "torsionPath" ); + + EditorSettings.beginGroup( "ObjectIcons" ); + EWorldEditor.fadeIcons = EditorSettings.value( "fadeIcons" ); + EWorldEditor.fadeIconsStartDist = EditorSettings.value( "fadeIconsStartDist" ); + EWorldEditor.fadeIconsEndDist = EditorSettings.value( "fadeIconsEndDist" ); + EWorldEditor.fadeIconsStartAlpha = EditorSettings.value( "fadeIconsStartAlpha" ); + EWorldEditor.fadeIconsEndAlpha = EditorSettings.value( "fadeIconsEndAlpha" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Grid" ); + EWorldEditor.gridSize = EditorSettings.value( "gridSize" ); + EWorldEditor.gridSnap = EditorSettings.value( "gridSnap" ); + EWorldEditor.gridColor = EditorSettings.value( "gridColor" ); + EWorldEditor.gridOriginColor = EditorSettings.value( "gridOriginColor" ); + EWorldEditor.gridMinorColor = EditorSettings.value( "gridMinorColor" ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Tools" ); + EWorldEditor.stickToGround = EditorSettings.value("snapGround"); //$pref::WorldEditor::snapGround; + EWorldEditor.setSoftSnap( EditorSettings.value("snapSoft") ); //$pref::WorldEditor::snapSoft + EWorldEditor.setSoftSnapSize( EditorSettings.value("snapSoftSize") ); //$pref::WorldEditor::snapSoftSize + EWorldEditor.boundingBoxCollision = EditorSettings.value("boundingBoxCollision"); //$pref::WorldEditor::boundingBoxCollision; + EWorldEditor.objectsUseBoxCenter = EditorSettings.value("objectsUseBoxCenter"); //$pref::WorldEditor::objectsUseBoxCenter; + EWorldEditor.dropAtScreenCenterScalar = EditorSettings.value("dropAtScreenCenterScalar"); + EWorldEditor.dropAtScreenCenterMax = EditorSettings.value("dropAtScreenCenterMax"); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Render" ); + EWorldEditor.renderObjHandle = EditorSettings.value("renderObjHandle"); //$pref::WorldEditor::renderObjHandle; + EWorldEditor.renderObjText = EditorSettings.value("renderObjText"); //$pref::WorldEditor::renderObjText; + EWorldEditor.renderPopupBackground = EditorSettings.value("renderPopupBackground"); //$pref::WorldEditor::renderPopupBackground; + EWorldEditor.renderSelectionBox = EditorSettings.value("renderSelectionBox"); //$pref::WorldEditor::renderSelectionBox; + EWorldEditor.showMousePopupInfo = EditorSettings.value("showMousePopupInfo"); //$pref::WorldEditor::showMousePopupInfo; + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Color" ); + EWorldEditor.dragRectColor = EditorSettings.value("dragRectColor"); //$pref::WorldEditor::dragRectColor; + EWorldEditor.objectTextColor = EditorSettings.value("objectTextColor"); //$pref::WorldEditor::objectTextColor; + EWorldEditor.objMouseOverColor = EditorSettings.value("objMouseOverColor"); //$pref::WorldEditor::objMouseOverColor; + EWorldEditor.objMouseOverSelectColor = EditorSettings.value("objMouseOverSelectColor"); //$pref::WorldEditor::objMouseOverSelectColor; + EWorldEditor.objSelectColor = EditorSettings.value("objSelectColor"); //$pref::WorldEditor::objSelectColor; + EWorldEditor.popupBackgroundColor = EditorSettings.value("popupBackgroundColor"); //$pref::WorldEditor::popupBackgroundColor; + EWorldEditor.popupTextColor = EditorSettings.value("popupTextColor"); //$pref::WorldEditor::popupTextColor; + EWorldEditor.selectionBoxColor = EditorSettings.value("selectionBoxColor"); //$pref::WorldEditor::selectionBoxColor; + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Images" ); + EWorldEditor.defaultHandle = EditorSettings.value("defaultHandle"); //$pref::WorldEditor::defaultHandle; + EWorldEditor.lockedHandle = EditorSettings.value("lockedHandle"); //$pref::WorldEditor::lockedHandle; + EWorldEditor.selectHandle = EditorSettings.value("selectHandle"); //$pref::WorldEditor::selectHandle; + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Docs" ); + EWorldEditor.documentationLocal = EditorSettings.value( "documentationLocal" ); + EWorldEditor.documentationURL = EditorSettings.value( "documentationURL" ); + EWorldEditor.documentationReference = EditorSettings.value( "documentationReference" ); + EWorldEditor.forumURL = EditorSettings.value( "forumURL" ); + EditorSettings.endGroup(); + + //EWorldEditor.planarMovement = $pref::WorldEditor::planarMovement; //<-- What is this used for? + + EditorSettings.endGroup(); // WorldEditor + + EditorSettings.beginGroup( "AxisGizmo", true ); + GlobalGizmoProfile.screenLength = EditorSettings.value("axisGizmoMaxScreenLen"); //$pref::WorldEditor::axisGizmoMaxScreenLen; + GlobalGizmoProfile.rotationSnap = EditorSettings.value("rotationSnap"); //$pref::WorldEditor::rotationSnap; + GlobalGizmoProfile.snapRotations = EditorSettings.value("snapRotations"); //$pref::WorldEditor::snapRotations; + GlobalGizmoProfile.rotateScalar = EditorSettings.value("mouseRotateScalar"); //$pref::WorldEditor::mouseRotateScalar; + GlobalGizmoProfile.scaleScalar = EditorSettings.value("mouseScaleScalar"); //$pref::WorldEditor::mouseScaleScalar; + GlobalGizmoProfile.renderWhenUsed = EditorSettings.value("renderWhenUsed"); + GlobalGizmoProfile.renderInfoText = EditorSettings.value("renderInfoText"); + + EditorSettings.beginGroup( "Grid" ); + GlobalGizmoProfile.gridColor = EditorSettings.value("gridColor"); //$pref::WorldEditor::gridColor; + GlobalGizmoProfile.gridSize = EditorSettings.value("gridSize"); //$pref::WorldEditor::gridSize; + GlobalGizmoProfile.snapToGrid = EditorSettings.value("snapToGrid"); //$pref::WorldEditor::snapToGrid; + GlobalGizmoProfile.renderPlane = EditorSettings.value("renderPlane"); //$pref::WorldEditor::renderPlane; + GlobalGizmoProfile.renderPlaneHashes = EditorSettings.value("renderPlaneHashes"); //$pref::WorldEditor::renderPlaneHashes; + GlobalGizmoProfile.planeDim = EditorSettings.value("planeDim"); //$pref::WorldEditor::planeDim; + EditorSettings.endGroup(); + + EditorSettings.endGroup(); // AxisGizmo +} + +function EditorGui::writeWorldEditorSettings(%this) +{ + EditorSettings.beginGroup( "WorldEditor", true ); + EditorSettings.setValue( "dropType", EWorldEditor.dropType ); //$pref::WorldEditor::dropType + EditorSettings.setValue( "undoLimit", EWorldEditor.undoLimit ); //$pref::WorldEditor::undoLimit + EditorSettings.setValue( "forceLoadDAE", EWorldEditor.forceLoadDAE ); //$pref::WorldEditor::forceLoadDAE + EditorSettings.setValue( "displayType", %this.currentDisplayType ); + EditorSettings.setValue( "orthoFOV", %this.currentOrthoFOV ); + EditorSettings.setValue( "orthoShowGrid", EWorldEditor.renderOrthoGrid ); + EditorSettings.setValue( "currentEditor", %this.currentEditor ); + EditorSettings.setvalue( "torsionPath", %this.torsionPath ); + + EditorSettings.beginGroup( "ObjectIcons" ); + EditorSettings.setValue( "fadeIcons", EWorldEditor.fadeIcons ); + EditorSettings.setValue( "fadeIconsStartDist", EWorldEditor.fadeIconsStartDist ); + EditorSettings.setValue( "fadeIconsEndDist", EWorldEditor.fadeIconsEndDist ); + EditorSettings.setValue( "fadeIconsStartAlpha", EWorldEditor.fadeIconsStartAlpha ); + EditorSettings.setValue( "fadeIconsEndAlpha", EWorldEditor.fadeIconsEndAlpha ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Grid" ); + EditorSettings.setValue( "gridSize", EWorldEditor.gridSize ); + EditorSettings.setValue( "gridSnap", EWorldEditor.gridSnap ); + EditorSettings.setValue( "gridColor", EWorldEditor.gridColor ); + EditorSettings.setValue( "gridOriginColor", EWorldEditor.gridOriginColor ); + EditorSettings.setValue( "gridMinorColor", EWorldEditor.gridMinorColor ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Tools" ); + EditorSettings.setValue( "snapGround", EWorldEditor.stickToGround ); //$Pref::WorldEditor::snapGround + EditorSettings.setValue( "snapSoft", EWorldEditor.getSoftSnap() ); //$Pref::WorldEditor::snapSoft + EditorSettings.setValue( "snapSoftSize", EWorldEditor.getSoftSnapSize() ); //$Pref::WorldEditor::snapSoftSize + EditorSettings.setValue( "boundingBoxCollision", EWorldEditor.boundingBoxCollision ); //$Pref::WorldEditor::boundingBoxCollision + EditorSettings.setValue( "objectsUseBoxCenter", EWorldEditor.objectsUseBoxCenter ); //$Pref::WorldEditor::objectsUseBoxCenter + EditorSettings.setValue( "dropAtScreenCenterScalar", EWorldEditor.dropAtScreenCenterScalar ); + EditorSettings.setValue( "dropAtScreenCenterMax", EWorldEditor.dropAtScreenCenterMax ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Render" ); + EditorSettings.setValue( "renderObjHandle", EWorldEditor.renderObjHandle ); //$Pref::WorldEditor::renderObjHandle + EditorSettings.setValue( "renderObjText", EWorldEditor.renderObjText ); //$Pref::WorldEditor::renderObjText + EditorSettings.setValue( "renderPopupBackground", EWorldEditor.renderPopupBackground ); //$Pref::WorldEditor::renderPopupBackground + EditorSettings.setValue( "renderSelectionBox", EWorldEditor.renderSelectionBox ); //$Pref::WorldEditor::renderSelectionBox + EditorSettings.setValue( "showMousePopupInfo", EWorldEditor.showMousePopupInfo ); //$Pref::WorldEditor::showMousePopupInfo + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Color" ); + EditorSettings.setValue( "dragRectColor", EWorldEditor.dragRectColor ); //$Pref::WorldEditor::dragRectColor + EditorSettings.setValue( "objectTextColor", EWorldEditor.objectTextColor ); //$Pref::WorldEditor::objectTextColor + EditorSettings.setValue( "objMouseOverColor", EWorldEditor.objMouseOverColor ); //$Pref::WorldEditor::objMouseOverColor + EditorSettings.setValue( "objMouseOverSelectColor",EWorldEditor.objMouseOverSelectColor );//$Pref::WorldEditor::objMouseOverSelectColor + EditorSettings.setValue( "objSelectColor", EWorldEditor.objSelectColor ); //$Pref::WorldEditor::objSelectColor + EditorSettings.setValue( "popupBackgroundColor", EWorldEditor.popupBackgroundColor ); //$Pref::WorldEditor::popupBackgroundColor + EditorSettings.setValue( "selectionBoxColor", EWorldEditor.selectionBoxColor ); //$Pref::WorldEditor::selectionBoxColor + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Images" ); + EditorSettings.setValue( "defaultHandle", EWorldEditor.defaultHandle ); //$Pref::WorldEditor::defaultHandle + EditorSettings.setValue( "selectHandle", EWorldEditor.selectHandle ); //$Pref::WorldEditor::selectHandle + EditorSettings.setValue( "lockedHandle", EWorldEditor.lockedHandle ); //$Pref::WorldEditor::lockedHandle + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "Docs" ); + EditorSettings.setValue( "documentationLocal", EWorldEditor.documentationLocal ); + EditorSettings.setValue( "documentationReference", EWorldEditor.documentationReference ); + EditorSettings.setValue( "documentationURL", EWorldEditor.documentationURL ); + EditorSettings.setValue( "forumURL", EWorldEditor.forumURL ); + EditorSettings.endGroup(); + + EditorSettings.endGroup(); // WorldEditor + + EditorSettings.beginGroup( "AxisGizmo", true ); + + EditorSettings.setValue( "axisGizmoMaxScreenLen", GlobalGizmoProfile.screenLength ); //$Pref::WorldEditor::axisGizmoMaxScreenLen + EditorSettings.setValue( "rotationSnap", GlobalGizmoProfile.rotationSnap ); //$Pref::WorldEditor::rotationSnap + EditorSettings.setValue( "snapRotations", GlobalGizmoProfile.snapRotations ); //$Pref::WorldEditor::snapRotations + EditorSettings.setValue( "mouseRotateScalar", GlobalGizmoProfile.rotateScalar ); //$Pref::WorldEditor::mouseRotateScalar + EditorSettings.setValue( "mouseScaleScalar", GlobalGizmoProfile.scaleScalar ); //$Pref::WorldEditor::mouseScaleScalar + EditorSettings.setValue( "renderWhenUsed", GlobalGizmoProfile.renderWhenUsed ); + EditorSettings.setValue( "renderInfoText", GlobalGizmoProfile.renderInfoText ); + + EditorSettings.beginGroup( "Grid" ); + EditorSettings.setValue( "gridColor", GlobalGizmoProfile.gridColor ); //$Pref::WorldEditor::gridColor + EditorSettings.setValue( "gridSize", GlobalGizmoProfile.gridSize ); //$Pref::WorldEditor::gridSize + EditorSettings.setValue( "snapToGrid", GlobalGizmoProfile.snapToGrid ); //$Pref::WorldEditor::snapToGrid + EditorSettings.setValue( "renderPlane", GlobalGizmoProfile.renderPlane ); //$Pref::WorldEditor::renderPlane + EditorSettings.setValue( "renderPlaneHashes", GlobalGizmoProfile.renderPlaneHashes );//$Pref::WorldEditor::renderPlaneHashes + EditorSettings.setValue( "planeDim", GlobalGizmoProfile.planeDim ); //$Pref::WorldEditor::planeDim + EditorSettings.endGroup(); + + EditorSettings.endGroup(); // AxisGizmo +} + +function EditorGui::readTerrainEditorSettings(%this) +{ + EditorSettings.beginGroup( "TerrainEditor", true ); + + ETerrainEditor.savedAction = EditorSettings.value("currentAction"); + + EditorSettings.beginGroup( "Brush" ); + ETerrainEditor.maxBrushSize = EditorSettings.value("maxBrushSize"); + ETerrainEditor.setBrushSize( EditorSettings.value("brushSize") ); + ETerrainEditor.setBrushType( EditorSettings.value("brushType") ); + ETerrainEditor.setBrushPressure( EditorSettings.value("brushPressure") ); + ETerrainEditor.setBrushSoftness( EditorSettings.value("brushSoftness") ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "ActionValues" ); + ETerrainEditor.adjustHeightVal = EditorSettings.value("adjustHeightVal"); + ETerrainEditor.setHeightVal = EditorSettings.value("setHeightVal"); + ETerrainEditor.scaleVal = EditorSettings.value("scaleVal"); + ETerrainEditor.smoothFactor = EditorSettings.value("smoothFactor"); + ETerrainEditor.noiseFactor = EditorSettings.value("noiseFactor"); + ETerrainEditor.softSelectRadius = EditorSettings.value("softSelectRadius"); + ETerrainEditor.softSelectFilter = EditorSettings.value("softSelectFilter"); + ETerrainEditor.softSelectDefaultFilter = EditorSettings.value("softSelectDefaultFilter"); + ETerrainEditor.setSlopeLimitMinAngle( EditorSettings.value("slopeMinAngle") ); + ETerrainEditor.setSlopeLimitMaxAngle( EditorSettings.value("slopeMaxAngle") ); + EditorSettings.endGroup(); + + EditorSettings.endGroup(); +} + +function EditorGui::writeTerrainEditorSettings(%this) +{ + EditorSettings.beginGroup( "TerrainEditor", true ); + + EditorSettings.setValue( "currentAction", ETerrainEditor.savedAction ); + + EditorSettings.beginGroup( "Brush" ); + EditorSettings.setValue( "maxBrushSize", ETerrainEditor.maxBrushSize ); + EditorSettings.setValue( "brushSize", ETerrainEditor.getBrushSize() ); + EditorSettings.setValue( "brushType", ETerrainEditor.getBrushType() ); + EditorSettings.setValue( "brushPressure", ETerrainEditor.getBrushPressure() ); + EditorSettings.setValue( "brushSoftness", ETerrainEditor.getBrushSoftness() ); + EditorSettings.endGroup(); + + EditorSettings.beginGroup( "ActionValues" ); + EditorSettings.setValue( "adjustHeightVal", ETerrainEditor.adjustHeightVal ); + EditorSettings.setValue( "setHeightVal", ETerrainEditor.setHeightVal ); + EditorSettings.setValue( "scaleVal", ETerrainEditor.scaleVal ); + EditorSettings.setValue( "smoothFactor", ETerrainEditor.smoothFactor ); + EditorSettings.setValue( "noiseFactor", ETerrainEditor.noiseFactor ); + EditorSettings.setValue( "softSelectRadius", ETerrainEditor.softSelectRadius ); + EditorSettings.setValue( "softSelectFilter", ETerrainEditor.softSelectFilter ); + EditorSettings.setValue( "softSelectDefaultFilter",ETerrainEditor.softSelectDefaultFilter ); + EditorSettings.setValue( "slopeMinAngle", ETerrainEditor.getSlopeLimitMinAngle() ); + EditorSettings.setValue( "slopeMaxAngle", ETerrainEditor.getSlopeLimitMaxAngle() ); + EditorSettings.endGroup(); + + EditorSettings.endGroup(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorRender.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorRender.ed.cs new file mode 100644 index 000000000..4fae4d11c --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorRender.ed.cs @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +//------------------------------------------------------------------------------ +// Console onEditorRender functions: +//------------------------------------------------------------------------------ +// Functions: +// - renderSphere([pos], [radius], <sphereLevel>); +// - renderCircle([pos], [normal], [radius], <segments>); +// - renderTriangle([pnt], [pnt], [pnt]); +// - renderLine([start], [end], <thickness>); +// +// Variables: +// - consoleFrameColor - line prims are rendered with this +// - consoleFillColor +// - consoleSphereLevel - level of polyhedron subdivision +// - consoleCircleSegments +// - consoleLineWidth +//------------------------------------------------------------------------------ + +function SpawnSphere::onEditorRender(%this, %editor, %selected, %expanded) +{ + if(%selected $= "true") + { + %editor.consoleFrameColor = "255 0 0"; + %editor.consoleFillColor = "0 160 0 95"; + %editor.renderSphere(%this.getWorldBoxCenter(), %this.radius, 1); + } +} + +//function Item::onEditorRender(%this, %editor, %selected, %expanded) +//{ +// if(%this.getDataBlock().getName() $= "MineDeployed") +// { +// %editor.consoleFillColor = "0 0 0 0"; +// %editor.consoleFrameColor = "255 0 0"; +// %editor.renderSphere(%this.getWorldBoxCenter(), 6, 1); +// } +//} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editorSettingsWindow.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editorSettingsWindow.ed.cs new file mode 100644 index 000000000..3af447854 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editorSettingsWindow.ed.cs @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function ESettingsWindow::startup( %this ) +{ + ESettingsWindowTabBook.selectPage( 0 ); + ESettingsWindowList.setSelectedById( 0 ); +} + +function ESettingsWindow::onWake( %this ) +{ +} + +function ESettingsWindow::hideDialog( %this ) +{ + %this.setVisible(false); +} + +function ESettingsWindow::ToggleVisibility() +{ + if ( ESettingsWindow.visible ) + { + ESettingsWindow.setVisible(false); + } + else + { + ESettingsWindow.setVisible(true); + ESettingsWindow.selectWindow(); + ESettingsWindow.setCollapseGroup(false); + } +} + +function ESettingsWindow::addTabPage( %this, %page ) +{ + ESettingsWindowTabBook.add( %page ); + ESettingsWindowList.addRow( ESettingsWindowTabBook.getSelectedPage(), %page.text ); + ESettingsWindowList.sort(0); +} + +//----------------------------------------------------------------------------- + +function ESettingsWindowList::onSelect( %this, %id, %text ) +{ + ESettingsWindowTabBook.selectPage( %id ); +} + +//----------------------------------------------------------------------------- +// Standard settings GUI classes. Editors may define their own methods of +// working with settings and are not required to use these. +//----------------------------------------------------------------------------- + +function ESettingsWindowCheckbox::onWake( %this ) +{ + %this.setStateOn( EditorSettings.value( %this.editorSettingsValue )); +} + +function ESettingsWindowCheckbox::onClick( %this ) +{ + EditorSettings.setValue( %this.editorSettingsValue, %this.getValue() ); + eval(%this.editorSettingsRead); +} + +//----------------------------------------------------------------------------- + +function ESettingsWindowTextEdit::onWake( %this ) +{ + %this.setText( EditorSettings.value( %this.editorSettingsValue )); +} + +function ESettingsWindowTextEdit::onValidate( %this ) +{ + EditorSettings.setValue( %this.editorSettingsValue, %this.getValue() ); + eval(%this.editorSettingsRead); +} + +function ESettingsWindowTextEdit::onGainFirstResponder( %this ) +{ + %this.selectAllText(); +} + +//----------------------------------------------------------------------------- + +function ESettingsWindowColor::apply( %this, %color ) +{ + EditorSettings.setValue( %this.editorSettingsValue, %color ); + eval(%this.editorSettingsRead); + + %this.findObjectByInternalName("ColorEdit", true).setText( %color); + %this.findObjectByInternalName("ColorButton", true).color = ColorIntToFloat( %color ); +} + +function ESettingsWindowColorEdit::onWake( %this ) +{ + %this.setText( EditorSettings.value( %this.getParent().editorSettingsValue )); +} + +function ESettingsWindowColorEdit::onValidate( %this ) +{ + %this.getParent().apply( %this.getValue() ); +} + +function ESettingsWindowColorEdit::onGainFirstResponder( %this ) +{ + %this.selectAllText(); +} + +function ESettingsWindowColorButton::onWake( %this ) +{ + %this.color = ColorIntToFloat( EditorSettings.value( %this.getParent().editorSettingsValue ) ); +} + +function ESettingsWindowColorButton::onClick( %this ) +{ + getColorI( ColorFloatToInt( %this.color ), %this.getId() @ ".apply", %this.getRoot() ); + //EditorSettings.setValue( %this.editorSettingsValue, %this.getValue() ); + //eval(%this.editorSettingsRead); +} + +function ESettingsWindowColorButton::apply( %this, %color ) +{ + %this.getParent().apply(%color); + echo("ESettingsWindowColorButton::apply(): " @ %color); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs new file mode 100644 index 000000000..0e2813d57 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -0,0 +1,823 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +function EWCreatorWindow::init( %this ) +{ + // Just so we can recall this method for testing changes + // without restarting. + if ( isObject( %this.array ) ) + %this.array.delete(); + + %this.array = new ArrayObject(); + %this.array.caseSensitive = true; + %this.setListView( true ); + + %this.beginGroup( "Environment" ); + + // Removed Prefab as there doesn't really seem to be a point in creating a blank one + //%this.registerMissionObject( "Prefab", "Prefab" ); + %this.registerMissionObject( "SkyBox", "Sky Box" ); + %this.registerMissionObject( "CloudLayer", "Cloud Layer" ); + %this.registerMissionObject( "BasicClouds", "Basic Clouds" ); + %this.registerMissionObject( "ScatterSky", "Scatter Sky" ); + %this.registerMissionObject( "Sun", "Basic Sun" ); + %this.registerMissionObject( "Lightning" ); + %this.registerMissionObject( "WaterBlock", "Water Block" ); + %this.registerMissionObject( "SFXEmitter", "Sound Emitter" ); + %this.registerMissionObject( "Precipitation" ); + %this.registerMissionObject( "ParticleEmitterNode", "Particle Emitter" ); + %this.registerMissionObject( "VolumetricFog", "Volumetric Fog" ); + %this.registerMissionObject( "RibbonNode", "Ribbon" ); + + // Legacy features. Users should use Ground Cover and the Forest Editor. + //%this.registerMissionObject( "fxShapeReplicator", "Shape Replicator" ); + //%this.registerMissionObject( "fxFoliageReplicator", "Foliage Replicator" ); + + %this.registerMissionObject( "PointLight", "Point Light" ); + %this.registerMissionObject( "SpotLight", "Spot Light" ); + %this.registerMissionObject( "GroundCover", "Ground Cover" ); + %this.registerMissionObject( "TerrainBlock", "Terrain Block" ); + %this.registerMissionObject( "GroundPlane", "Ground Plane" ); + %this.registerMissionObject( "WaterPlane", "Water Plane" ); + %this.registerMissionObject( "PxCloth", "Cloth" ); + %this.registerMissionObject( "ForestWindEmitter", "Wind Emitter" ); + + %this.registerMissionObject( "DustEmitter", "Dust Emitter" ); + %this.registerMissionObject( "DustSimulation", "Dust Simulation" ); + %this.registerMissionObject( "DustEffecter", "Dust Effecter" ); + + %this.endGroup(); + + %this.beginGroup( "Level" ); + + %this.registerMissionObject( "MissionArea", "Mission Area" ); + %this.registerMissionObject( "Path" ); + %this.registerMissionObject( "Marker", "Path Node" ); + %this.registerMissionObject( "Trigger" ); + %this.registerMissionObject( "PhysicalZone", "Physical Zone" ); + %this.registerMissionObject( "Camera" ); + %this.registerMissionObject( "LevelInfo", "Level Info" ); + %this.registerMissionObject( "TimeOfDay", "Time of Day" ); + %this.registerMissionObject( "Zone", "Zone" ); + %this.registerMissionObject( "Portal", "Zone Portal" ); + %this.registerMissionObject( "SpawnSphere", "Player Spawn Sphere", "PlayerDropPoint" ); + %this.registerMissionObject( "SpawnSphere", "Observer Spawn Sphere", "ObserverDropPoint" ); + %this.registerMissionObject( "SFXSpace", "Sound Space" ); + %this.registerMissionObject( "OcclusionVolume", "Occlusion Volume" ); + %this.registerMissionObject( "AccumulationVolume", "Accumulation Volume" ); + %this.registerMissionObject( "Entity", "Entity" ); + + %this.endGroup(); + + %this.beginGroup( "System" ); + + %this.registerMissionObject( "SimGroup" ); + + %this.endGroup(); + + %this.beginGroup( "ExampleObjects" ); + + %this.registerMissionObject( "RenderObjectExample" ); + %this.registerMissionObject( "RenderMeshExample" ); + %this.registerMissionObject( "RenderShapeExample" ); + + %this.endGroup(); +} + +function EWCreatorWindow::onWake( %this ) +{ + CreatorTabBook.selectPage( 0 ); + CreatorTabBook.onTabSelected( "Scripted" ); +} + +function EWCreatorWindow::beginGroup( %this, %group ) +{ + %this.currentGroup = %group; +} + +function EWCreatorWindow::endGroup( %this, %group ) +{ + %this.currentGroup = ""; +} + +function EWCreatorWindow::getCreateObjectPosition() +{ + %focusPoint = LocalClientConnection.getControlObject().getLookAtPoint(); + if( %focusPoint $= "" ) + return "0 0 0"; + else + return getWord( %focusPoint, 1 ) SPC getWord( %focusPoint, 2 ) SPC getWord( %focusPoint, 3 ); +} + +function EWCreatorWindow::registerMissionObject( %this, %class, %name, %buildfunc, %group ) +{ + if( !isClass(%class) ) + return; + + if ( %name $= "" ) + %name = %class; + if ( %this.currentGroup !$= "" && %group $= "" ) + %group = %this.currentGroup; + + if ( %class $= "" || %group $= "" ) + { + warn( "EWCreatorWindow::registerMissionObject, invalid parameters!" ); + return; + } + + %args = new ScriptObject(); + %args.val[0] = %class; + %args.val[1] = %name; + %args.val[2] = %buildfunc; + + %this.array.push_back( %group, %args ); +} + +function EWCreatorWindow::getNewObjectGroup( %this ) +{ + return %this.objectGroup; +} + +function EWCreatorWindow::setNewObjectGroup( %this, %group ) +{ + if( %this.objectGroup ) + { + %oldItemId = EditorTree.findItemByObjectId( %this.objectGroup ); + if( %oldItemId > 0 ) + EditorTree.markItem( %oldItemId, false ); + } + + %group = %group.getID(); + %this.objectGroup = %group; + %itemId = EditorTree.findItemByObjectId( %group ); + EditorTree.markItem( %itemId ); +} + +function EWCreatorWindow::createStatic( %this, %file ) +{ + if ( !$missionRunning ) + return; + + if( !isObject(%this.objectGroup) ) + %this.setNewObjectGroup( MissionGroup ); + + %objId = new TSStatic() + { + shapeName = %file; + position = %this.getCreateObjectPosition(); + parentGroup = %this.objectGroup; + }; + + %this.onObjectCreated( %objId ); +} + +function EWCreatorWindow::createPrefab( %this, %file ) +{ + if ( !$missionRunning ) + return; + + if( !isObject(%this.objectGroup) ) + %this.setNewObjectGroup( MissionGroup ); + + %objId = new Prefab() + { + filename = %file; + position = %this.getCreateObjectPosition(); + parentGroup = %this.objectGroup; + }; + + %this.onObjectCreated( %objId ); +} + +function EWCreatorWindow::createObject( %this, %cmd ) +{ + if ( !$missionRunning ) + return; + + if( !isObject(%this.objectGroup) ) + %this.setNewObjectGroup( MissionGroup ); + + pushInstantGroup(); + %objId = eval(%cmd); + popInstantGroup(); + + if( isObject( %objId ) ) + %this.onFinishCreateObject( %objId ); + + return %objId; +} + +function EWCreatorWindow::onFinishCreateObject( %this, %objId ) +{ + %this.objectGroup.add( %objId ); + + if( %objId.isMemberOfClass( "SceneObject" ) ) + { + %objId.position = %this.getCreateObjectPosition(); + + //flush new position + %objId.setTransform( %objId.getTransform() ); + } + + %this.onObjectCreated( %objId ); +} + +function EWCreatorWindow::onObjectCreated( %this, %objId ) +{ + // Can we submit an undo action? + if ( isObject( %objId ) ) + MECreateUndoAction::submit( %objId ); + + EditorTree.clearSelection(); + EWorldEditor.clearSelection(); + EWorldEditor.selectObject( %objId ); + + // When we drop the selection don't store undo + // state for it... the creation deals with it. + EWorldEditor.dropSelection( true ); +} + +function CreatorTabBook::onTabSelected( %this, %text, %idx ) +{ + if ( %this.isAwake() ) + { + EWCreatorWindow.tab = %text; + EWCreatorWindow.navigate( "" ); + } +} + +function EWCreatorWindow::navigate( %this, %address ) +{ + CreatorIconArray.frozen = true; + CreatorIconArray.clear(); + CreatorPopupMenu.clear(); + + if ( %this.tab $= "Scripted" ) + { + %category = getWord( %address, 1 ); + %dataGroup = "DataBlockGroup"; + + for ( %i = 0; %i < %dataGroup.getCount(); %i++ ) + { + %obj = %dataGroup.getObject(%i); + // echo ("Obj: " @ %obj.getName() @ " - " @ %obj.category ); + + if ( %obj.category $= "" && %obj.category == 0 ) + continue; + + // Add category to popup menu if not there already + if ( CreatorPopupMenu.findText( %obj.category ) == -1 ) + CreatorPopupMenu.add( %obj.category ); + + if ( %address $= "" ) + { + %ctrl = %this.findIconCtrl( %obj.category ); + if ( %ctrl == -1 ) + { + %this.addFolderIcon( %obj.category ); + } + } + else if ( %address $= %obj.category ) + { + %ctrl = %this.findIconCtrl( %obj.getName() ); + if ( %ctrl == -1 ) + %this.addShapeIcon( %obj ); + } + } + + //Add a separate folder for Game Objects + if(isClass("Entity")) + { + if(%address $= "") + { + %this.addFolderIcon("GameObjects"); + } + else + { + //find all GameObjectAssets + %assetQuery = new AssetQuery(); + if(!AssetDatabase.findAssetType(%assetQuery, "GameObjectAsset")) + return 0; //if we didn't find ANY, just exit + + %count = %assetQuery.getCount(); + + for(%i=0; %i < %count; %i++) + { + %assetId = %assetQuery.getAsset(%i); + + %gameObjectAsset = AssetDatabase.acquireAsset(%assetId); + + if(isFile(%gameObjectAsset.TAMLFilePath)) + { + %this.addGameObjectIcon( %gameObjectAsset.gameObjectName ); + } + } + } + } + } + + if ( %this.tab $= "Meshes" ) + { + %fullPath = findFirstFileMultiExpr( getFormatExtensions() ); + + while ( %fullPath !$= "" ) + { + if (strstr(%fullPath, "cached.dts") != -1) + { + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); + continue; + } + + %fullPath = makeRelativePath( %fullPath, getMainDotCSDir() ); + %splitPath = strreplace( %fullPath, " ", "_" ); + %splitPath = strreplace( %splitPath, "/", " " ); + if( getWord(%splitPath, 0) $= "tools" ) + { + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); + continue; + } + + %dirCount = getWordCount( %splitPath ) - 1; + + %pathFolders = getWords( %splitPath, 0, %dirCount - 1 ); + + // Add this file's path (parent folders) to the + // popup menu if it isn't there yet. + %temp = strreplace( %pathFolders, " ", "/" ); + %temp = strreplace( %temp, "_", " " ); + %r = CreatorPopupMenu.findText( %temp ); + if ( %r == -1 ) + { + CreatorPopupMenu.add( %temp ); + } + + // Is this file in the current folder? + if ( stricmp( %pathFolders, %address ) == 0 ) + { + %this.addStaticIcon( %fullPath ); + } + // Then is this file in a subfolder we need to add + // a folder icon for? + else + { + %wordIdx = 0; + %add = false; + + if ( %address $= "" ) + { + %add = true; + %wordIdx = 0; + } + else + { + for ( ; %wordIdx < %dirCount; %wordIdx++ ) + { + %temp = getWords( %splitPath, 0, %wordIdx ); + if ( stricmp( %temp, %address ) == 0 ) + { + %add = true; + %wordIdx++; + break; + } + } + } + + if ( %add == true ) + { + %folder = getWord( %splitPath, %wordIdx ); + + %ctrl = %this.findIconCtrl( %folder ); + if ( %ctrl == -1 ) + %this.addFolderIcon( %folder ); + } + } + + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); + } + } + + if ( %this.tab $= "Level" ) + { + // Add groups to popup menu + %array = %this.array; + %array.sortk(); + + %count = %array.count(); + + if ( %count > 0 ) + { + %lastGroup = ""; + + for ( %i = 0; %i < %count; %i++ ) + { + %group = %array.getKey( %i ); + + if ( %group !$= %lastGroup ) + { + CreatorPopupMenu.add( %group ); + + if ( %address $= "" ) + %this.addFolderIcon( %group ); + } + + if ( %address $= %group ) + { + %args = %array.getValue( %i ); + %class = %args.val[0]; + %name = %args.val[1]; + %func = %args.val[2]; + + %this.addMissionObjectIcon( %class, %name, %func ); + } + + %lastGroup = %group; + } + } + } + + if ( %this.tab $= "Prefabs" ) + { + %expr = "*.prefab"; + %fullPath = findFirstFile( %expr ); + + while ( %fullPath !$= "" ) + { + %fullPath = makeRelativePath( %fullPath, getMainDotCSDir() ); + %splitPath = strreplace( %fullPath, " ", "_" ); + %splitPath = strreplace( %splitPath, "/", " " ); + if( getWord(%splitPath, 0) $= "tools" ) + { + %fullPath = findNextFile( %expr ); + continue; + } + + %dirCount = getWordCount( %splitPath ) - 1; + + %pathFolders = getWords( %splitPath, 0, %dirCount - 1 ); + + // Add this file's path (parent folders) to the + // popup menu if it isn't there yet. + %temp = strreplace( %pathFolders, " ", "/" ); + %temp = strreplace( %temp, "_", " " ); + %r = CreatorPopupMenu.findText( %temp ); + if ( %r == -1 ) + { + CreatorPopupMenu.add( %temp ); + } + + // Is this file in the current folder? + if ( (%dirCount == 0 && %address $= "") || stricmp( %pathFolders, %address ) == 0 ) + { + %this.addPrefabIcon( %fullPath ); + } + // Then is this file in a subfolder we need to add + // a folder icon for? + else + { + %wordIdx = 0; + %add = false; + + if ( %address $= "" ) + { + %add = true; + %wordIdx = 0; + } + else + { + for ( ; %wordIdx < %dirCount; %wordIdx++ ) + { + %temp = getWords( %splitPath, 0, %wordIdx ); + if ( stricmp( %temp, %address ) == 0 ) + { + %add = true; + %wordIdx++; + break; + } + } + } + + if ( %add == true ) + { + %folder = getWord( %splitPath, %wordIdx ); + + %ctrl = %this.findIconCtrl( %folder ); + if ( %ctrl == -1 ) + %this.addFolderIcon( %folder ); + } + } + + %fullPath = findNextFile( %expr ); + } + } + + CreatorIconArray.sort( "alphaIconCompare" ); + + for ( %i = 0; %i < CreatorIconArray.getCount(); %i++ ) + { + CreatorIconArray.getObject(%i).autoSize = false; + } + + CreatorIconArray.frozen = false; + CreatorIconArray.refresh(); + + // Recalculate the array for the parent guiScrollCtrl + CreatorIconArray.getParent().computeSizes(); + + %this.address = %address; + + CreatorPopupMenu.sort(); + + %str = strreplace( %address, " ", "/" ); + %r = CreatorPopupMenu.findText( %str ); + if ( %r != -1 ) + CreatorPopupMenu.setSelected( %r, false ); + else + CreatorPopupMenu.setText( %str ); + CreatorPopupMenu.tooltip = %str; +} + +function EWCreatorWindow::navigateDown( %this, %folder ) +{ + if ( %this.address $= "" ) + %address = %folder; + else + %address = %this.address SPC %folder; + + // Because this is called from an IconButton::onClick command + // we have to wait a tick before actually calling navigate, else + // we would delete the button out from under itself. + %this.schedule( 1, "navigate", %address ); +} + +function EWCreatorWindow::navigateUp( %this ) +{ + %count = getWordCount( %this.address ); + + if ( %count == 0 ) + return; + + if ( %count == 1 ) + %address = ""; + else + %address = getWords( %this.address, 0, %count - 2 ); + + %this.navigate( %address ); +} + +function EWCreatorWindow::setListView( %this, %noupdate ) +{ + //CreatorIconArray.clear(); + //CreatorIconArray.setVisible( false ); + + CreatorIconArray.setVisible( true ); + %this.contentCtrl = CreatorIconArray; + %this.isList = true; + + if ( %noupdate == true ) + %this.navigate( %this.address ); +} + +//function EWCreatorWindow::setIconView( %this ) +//{ + //echo( "setIconView" ); + // + //CreatorIconStack.clear(); + //CreatorIconStack.setVisible( false ); + // + //CreatorIconArray.setVisible( true ); + //%this.contentCtrl = CreatorIconArray; + //%this.isList = false; + // + //%this.navigate( %this.address ); +//} + +function EWCreatorWindow::findIconCtrl( %this, %name ) +{ + for ( %i = 0; %i < %this.contentCtrl.getCount(); %i++ ) + { + %ctrl = %this.contentCtrl.getObject( %i ); + if ( %ctrl.text $= %name ) + return %ctrl; + } + + return -1; +} + +function EWCreatorWindow::createIcon( %this ) +{ + %ctrl = new GuiIconButtonCtrl() + { + profile = "GuiCreatorIconButtonProfile"; + buttonType = "radioButton"; + groupNum = "-1"; + }; + + if ( %this.isList ) + { + %ctrl.iconLocation = "Left"; + %ctrl.textLocation = "Right"; + %ctrl.extent = "348 19"; + %ctrl.textMargin = 8; + %ctrl.buttonMargin = "2 2"; + %ctrl.autoSize = true; + } + else + { + %ctrl.iconLocation = "Center"; + %ctrl.textLocation = "Bottom"; + %ctrl.extent = "40 40"; + } + + return %ctrl; +} + +function EWCreatorWindow::addFolderIcon( %this, %text ) +{ + %ctrl = %this.createIcon(); + + %ctrl.altCommand = "EWCreatorWindow.navigateDown(\"" @ %text @ "\");"; + %ctrl.iconBitmap = "tools/gui/images/folder.png"; + %ctrl.text = %text; + %ctrl.tooltip = %text; + %ctrl.class = "CreatorFolderIconBtn"; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function EWCreatorWindow::addMissionObjectIcon( %this, %class, %name, %buildfunc ) +{ + %ctrl = %this.createIcon(); + + // If we don't find a specific function for building an + // object then fall back to the stock one + %method = "build" @ %buildfunc; + if( !ObjectBuilderGui.isMethod( %method ) ) + %method = "build" @ %class; + + if( !ObjectBuilderGui.isMethod( %method ) ) + %cmd = "return new " @ %class @ "();"; + else + %cmd = "ObjectBuilderGui." @ %method @ "();"; + + %ctrl.altCommand = "ObjectBuilderGui.newObjectCallback = \"EWCreatorWindow.onFinishCreateObject\"; EWCreatorWindow.createObject( \"" @ %cmd @ "\" );"; + %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( %class ); + %ctrl.text = %name; + %ctrl.class = "CreatorMissionObjectIconBtn"; + %ctrl.tooltip = %class; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function EWCreatorWindow::addShapeIcon( %this, %datablock ) +{ + %ctrl = %this.createIcon(); + + %name = %datablock.getName(); + %class = %datablock.getClassName(); + %cmd = %class @ "::create(" @ %name @ ");"; + + %shapePath = ( %datablock.shapeFile !$= "" ) ? %datablock.shapeFile : %datablock.shapeName; + + %createCmd = "EWCreatorWindow.createObject( \\\"" @ %cmd @ "\\\" );"; + %ctrl.altCommand = "ColladaImportDlg.showDialog( \"" @ %shapePath @ "\", \"" @ %createCmd @ "\" );"; + + %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( %class ); + %ctrl.text = %name; + %ctrl.class = "CreatorShapeIconBtn"; + %ctrl.tooltip = %name; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function EWCreatorWindow::addStaticIcon( %this, %fullPath ) +{ + %ctrl = %this.createIcon(); + + %ext = fileExt( %fullPath ); + %file = fileBase( %fullPath ); + %fileLong = %file @ %ext; + %tip = %fileLong NL + "Size: " @ fileSize( %fullPath ) / 1000.0 SPC "KB" NL + "Date Created: " @ fileCreatedTime( %fullPath ) NL + "Last Modified: " @ fileModifiedTime( %fullPath ); + + %createCmd = "EWCreatorWindow.createStatic( \\\"" @ %fullPath @ "\\\" );"; + %ctrl.altCommand = "ColladaImportDlg.showDialog( \"" @ %fullPath @ "\", \"" @ %createCmd @ "\" );"; + + %ctrl.iconBitmap = ( ( %ext $= ".dts" ) ? EditorIconRegistry::findIconByClassName( "TSStatic" ) : "tools/gui/images/iconCollada" ); + %ctrl.text = %file; + %ctrl.class = "CreatorStaticIconBtn"; + %ctrl.tooltip = %tip; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function EWCreatorWindow::addPrefabIcon( %this, %fullPath ) +{ + %ctrl = %this.createIcon(); + + %ext = fileExt( %fullPath ); + %file = fileBase( %fullPath ); + %fileLong = %file @ %ext; + %tip = %fileLong NL + "Size: " @ fileSize( %fullPath ) / 1000.0 SPC "KB" NL + "Date Created: " @ fileCreatedTime( %fullPath ) NL + "Last Modified: " @ fileModifiedTime( %fullPath ); + + %ctrl.altCommand = "EWCreatorWindow.createPrefab( \"" @ %fullPath @ "\" );"; + %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( "Prefab" ); + %ctrl.text = %file; + %ctrl.class = "CreatorPrefabIconBtn"; + %ctrl.tooltip = %tip; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function EWCreatorWindow::addGameObjectIcon( %this, %gameObjectName ) +{ + %ctrl = %this.createIcon(); + + %ctrl.altCommand = "spawnGameObject( \"" @ %gameObjectName @ "\", true );"; + %ctrl.iconBitmap = EditorIconRegistry::findIconByClassName( "Prefab" ); + %ctrl.text = %gameObjectName; + %ctrl.class = "CreatorGameObjectIconBtn"; + %ctrl.tooltip = "Spawn the " @ %gameObjectName @ " GameObject"; + + %ctrl.buttonType = "radioButton"; + %ctrl.groupNum = "-1"; + + %this.contentCtrl.addGuiControl( %ctrl ); +} + +function CreatorPopupMenu::onSelect( %this, %id, %text ) +{ + %split = strreplace( %text, "/", " " ); + EWCreatorWindow.navigate( %split ); +} + +function alphaIconCompare( %a, %b ) +{ + if ( %a.class $= "CreatorFolderIconBtn" ) + if ( %b.class !$= "CreatorFolderIconBtn" ) + return -1; + + if ( %b.class $= "CreatorFolderIconBtn" ) + if ( %a.class !$= "CreatorFolderIconBtn" ) + return 1; + + %result = stricmp( %a.text, %b.text ); + return %result; +} + +// Generic create object helper for use from the console. + +function genericCreateObject( %class ) +{ + if ( !isClass( %class ) ) + { + warn( "createObject( " @ %class @ " ) - Was not a valid class." ); + return; + } + + %cmd = "return new " @ %class @ "();"; + + %obj = EWCreatorWindow.createObject( %cmd ); + + // In case the caller wants it. + return %obj; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/missionArea.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/missionArea.ed.cs new file mode 100644 index 000000000..a2b3f90a9 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/missionArea.ed.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function AreaEditor::onUpdate(%this, %area) +{ + AreaEditingText.setValue( "X: " @ getWord(%area,0) @ " Y: " @ getWord(%area,1) @ " W: " @ getWord(%area,2) @ " H: " @ getWord(%area,3)); +} + +function AreaEditor::onWorldOffset(%this, %offset) +{ +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs new file mode 100644 index 000000000..63d185541 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs @@ -0,0 +1,479 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +/// The texture filename filter used with OpenFileDialog. +$TerrainEditor::TextureFileSpec = "Image Files (*.png, *.jpg, *.dds)|*.png;*.jpg;*.dds|All Files (*.*)|*.*|"; + +function TerrainEditor::init( %this ) +{ + %this.attachTerrain(); + %this.setBrushSize( 9, 9 ); + + new PersistenceManager( ETerrainPersistMan ); +} + +/// +function EPainter_TerrainMaterialUpdateCallback( %mat, %matIndex ) +{ + // Skip over a bad selection. + if ( %matIndex == -1 || !isObject( %mat ) ) + return; + + // Update the material and the UI. + ETerrainEditor.updateMaterial( %matIndex, %mat.getInternalName() ); + EPainter.setup( %matIndex ); +} + +function EPainter_TerrainMaterialAddCallback( %mat, %matIndex ) +{ + // Ignore bad materials. + if ( !isObject( %mat ) ) + return; + + // Add it and update the UI. + ETerrainEditor.addMaterial( %mat.getInternalName() ); + EPainter.setup( %matIndex ); +} + +function TerrainEditor::setPaintMaterial( %this, %matIndex, %terrainMat ) +{ + assert( isObject( %terrainMat ), "TerrainEditor::setPaintMaterial - Got bad material!" ); + + ETerrainEditor.paintIndex = %matIndex; + ETerrainMaterialSelected.selectedMatIndex = %matIndex; + ETerrainMaterialSelected.selectedMat = %terrainMat; + ETerrainMaterialSelected.bitmap = %terrainMat.diffuseMap; + ETerrainMaterialSelectedEdit.Visible = isObject(%terrainMat); + TerrainTextureText.text = %terrainMat.getInternalName(); + ProceduralTerrainPainterDescription.text = "Generate "@ %terrainMat.getInternalName() @" layer"; +} + +function TerrainEditor::setup( %this ) +{ + %action = %this.savedAction; + %desc = %this.savedActionDesc; + if ( %this.savedAction $= "" ) + { + %action = brushAdjustHeight; + } + + %this.switchAction( %action ); +} + +function EPainter::updateLayers( %this, %matIndex ) +{ + // Default to whatever was selected before. + if ( %matIndex $= "" ) + %matIndex = ETerrainEditor.paintIndex; + + // The material string is a newline seperated string of + // TerrainMaterial internal names which we can use to find + // the actual material data in TerrainMaterialSet. + + %mats = ETerrainEditor.getMaterials(); + + %matList = %this-->theMaterialList; + %matList.deleteAllObjects(); + %listWidth = getWord( %matList.getExtent(), 0 ); + + for( %i = 0; %i < getRecordCount( %mats ); %i++ ) + { + %matInternalName = getRecord( %mats, %i ); + %mat = TerrainMaterialSet.findObjectByInternalName( %matInternalName ); + + // Is there no material info for this slot? + if ( !isObject( %mat ) ) + continue; + + %index = %matList.getCount(); + %command = "ETerrainEditor.setPaintMaterial( " @ %index @ ", " @ %mat @ " );"; + %altCommand = "TerrainMaterialDlg.show( " @ %index @ ", " @ %mat @ ", EPainter_TerrainMaterialUpdateCallback );"; + + %ctrl = new GuiIconButtonCtrl() + { + class = "EPainterIconBtn"; + internalName = "EPainterMaterialButton" @ %i; + profile = "GuiCreatorIconButtonProfile"; + iconLocation = "Left"; + textLocation = "Right"; + extent = %listWidth SPC "46"; + textMargin = 5; + buttonMargin = "4 4"; + buttonType = "RadioButton"; + sizeIconToButton = true; + makeIconSquare = true; + tooltipprofile = "ToolsGuiToolTipProfile"; + command = %command; + altCommand = %altCommand; + useMouseEvents = true; + + new GuiBitmapButtonCtrl() + { + bitmap = "tools/gui/images/delete"; + buttonType = "PushButton"; + HorizSizing = "left"; + VertSizing = "bottom"; + position = ( %listwidth - 20 ) SPC "26"; + Extent = "17 17"; + command = "EPainter.showMaterialDeleteDlg( " @ %matInternalName @ " );"; + }; + }; + + %ctrl.setText( %matInternalName ); + %ctrl.setBitmap( %mat.diffuseMap ); + + %tooltip = %matInternalName; + if(%i < 9) + %tooltip = %tooltip @ " (" @ (%i+1) @ ")"; + else if(%i == 9) + %tooltip = %tooltip @ " (0)"; + %ctrl.tooltip = %tooltip; + + %matList.add( %ctrl ); + } + + %matCount = %matList.getCount(); + + // Add one more layer as the 'add new' layer. + %ctrl = new GuiIconButtonCtrl() + { + profile = "GuiCreatorIconButtonProfile"; + iconBitmap = "~/worldEditor/images/terrainpainter/new_layer_icon"; + iconLocation = "Left"; + textLocation = "Right"; + extent = %listWidth SPC "46"; + textMargin = 5; + buttonMargin = "4 4"; + buttonType = "PushButton"; + sizeIconToButton = true; + makeIconSquare = true; + tooltipprofile = "ToolsGuiToolTipProfile"; + text = "New Layer"; + tooltip = "New Layer"; + command = "TerrainMaterialDlg.show( " @ %matCount @ ", 0, EPainter_TerrainMaterialAddCallback );"; + }; + %matList.add( %ctrl ); + + // Make sure our selection is valid and that we're + // not selecting the 'New Layer' button. + + if( %matIndex < 0 ) + return; + if( %matIndex >= %matCount ) + %matIndex = 0; + + // To make things simple... click the paint material button to + // active it and initialize other state. + %ctrl = %matList.getObject( %matIndex ); + %ctrl.performClick(); +} + +function EPainter::showMaterialDeleteDlg( %this, %matInternalName ) +{ + MessageBoxYesNo( "Confirmation", + "Really remove material '" @ %matInternalName @ "' from the terrain?", + %this @ ".removeMaterial( " @ %matInternalName @ " );", "" ); +} + +function EPainter::removeMaterial( %this, %matInternalName ) +{ + %selIndex = ETerrainEditor.paintIndex - 1; + + // Remove the material from the terrain. + + %index = ETerrainEditor.getMaterialIndex( %matInternalName ); + if( %index != -1 ) + ETerrainEditor.removeMaterial( %index ); + + // Update the material list. + + %this.updateLayers( %selIndex ); +} + +function EPainter::setup( %this, %matIndex ) +{ + // Update the layer listing. + %this.updateLayers( %matIndex ); + + // Automagically put us into material paint mode. + ETerrainEditor.currentMode = "paint"; + ETerrainEditor.selectionHidden = true; + ETerrainEditor.currentAction = paintMaterial; + ETerrainEditor.currentActionDesc = "Paint material on terrain"; + ETerrainEditor.setAction( ETerrainEditor.currentAction ); + EditorGuiStatusBar.setInfo(ETerrainEditor.currentActionDesc); + ETerrainEditor.renderVertexSelection = true; +} + +function onNeedRelight() +{ + if( RelightMessage.visible == false ) + RelightMessage.visible = true; +} + +function TerrainEditor::onGuiUpdate(%this, %text) +{ + %minHeight = getWord(%text, 1); + %avgHeight = getWord(%text, 2); + %maxHeight = getWord(%text, 3); + + %mouseBrushInfo = " (Mouse) #: " @ getWord(%text, 0) @ " avg: " @ %avgHeight @ " " @ ETerrainEditor.currentAction; + %selectionInfo = " (Selected) #: " @ getWord(%text, 4) @ " avg: " @ getWord(%text, 5); + + TEMouseBrushInfo.setValue(%mouseBrushInfo); + TEMouseBrushInfo1.setValue(%mouseBrushInfo); + TESelectionInfo.setValue(%selectionInfo); + TESelectionInfo1.setValue(%selectionInfo); + + EditorGuiStatusBar.setSelection("min: " @ %minHeight @ " avg: " @ %avgHeight @ " max: " @ %maxHeight); +} + +function TerrainEditor::onBrushChanged( %this ) +{ + EditorGui.currentEditor.syncBrushInfo(); +} + +function TerrainEditor::toggleBrushType( %this, %brush ) +{ + %this.setBrushType( %brush.internalName ); +} + +function TerrainEditor::offsetBrush(%this, %x, %y) +{ + %curPos = %this.getBrushPos(); + %this.setBrushPos(getWord(%curPos, 0) + %x, getWord(%curPos, 1) + %y); +} + +function TerrainEditor::onActiveTerrainChange(%this, %newTerrain) +{ + // Need to refresh the terrain painter. + if ( EditorGui.currentEditor.getId() == TerrainPainterPlugin.getId() ) + EPainter.setup(ETerrainEditor.paintIndex); +} + +function TerrainEditor::getActionDescription( %this, %action ) +{ + switch$( %action ) + { + case "brushAdjustHeight": + return "Adjust terrain height up or down."; + + case "raiseHeight": + return "Raise terrain height."; + + case "lowerHeight": + return "Lower terrain height."; + + case "smoothHeight": + return "Smooth terrain."; + + case "paintNoise": + return "Modify terrain height using noise."; + + case "flattenHeight": + return "Flatten terrain."; + + case "setHeight": + return "Set terrain height to defined value."; + + case "setEmpty": + return "Remove terrain collision."; + + case "clearEmpty": + return "Add back terrain collision."; + + default: + return ""; + } +} + +/// This is only ment for terrain editing actions and not +/// processed actions or the terrain material painting action. +function TerrainEditor::switchAction( %this, %action ) +{ + %actionDesc = %this.getActionDescription(%action); + + %this.currentMode = "paint"; + %this.selectionHidden = true; + %this.currentAction = %action; + %this.currentActionDesc = %actionDesc; + %this.savedAction = %action; + %this.savedActionDesc = %actionDesc; + + if ( %action $= "setEmpty" || + %action $= "clearEmpty" || + %action $= "setHeight" ) + %this.renderSolidBrush = true; + else + %this.renderSolidBrush = false; + + EditorGuiStatusBar.setInfo(%actionDesc); + + %this.setAction( %this.currentAction ); +} + +function TerrainEditor::onSmoothHeightmap( %this ) +{ + if ( !%this.getActiveTerrain() ) + return; + + // Show the dialog first and let the user + // set the smoothing parameters. + + + + // Now create the terrain smoothing action to + // get the work done and perform later undos. + %action = new TerrainSmoothAction(); + %action.smooth( %this.getActiveTerrain(), 1.0, 1 ); + %action.addToManager( Editor.getUndoManager() ); +} + +function TerrainEditor::onMaterialUndo( %this ) +{ + // Update the gui to reflect the current materials. + EPainter.updateLayers(); +} + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + +function TerrainEditorSettingsGui::onWake(%this) +{ + TESoftSelectFilter.setValue(ETerrainEditor.softSelectFilter); +} + +function TerrainEditorSettingsGui::onSleep(%this) +{ + ETerrainEditor.softSelectFilter = TESoftSelectFilter.getValue(); +} + +function TESettingsApplyButton::onAction(%this) +{ + ETerrainEditor.softSelectFilter = TESoftSelectFilter.getValue(); + ETerrainEditor.resetSelWeights(true); + ETerrainEditor.processAction("softSelect"); +} + +function getPrefSetting(%pref, %default) +{ + // + if(%pref $= "") + return(%default); + else + return(%pref); +} + +function TerrainEditorPlugin::setEditorFunction(%this) +{ + %terrainExists = parseMissionGroup( "TerrainBlock" ); + + if( %terrainExists == false ) + MessageBoxYesNoCancel("No Terrain","Would you like to create a New Terrain?", "Canvas.pushDialog(CreateNewTerrainGui);"); + + return %terrainExists; +} + +function TerrainPainterPlugin::setEditorFunction(%this, %overrideGroup) +{ + %terrainExists = parseMissionGroup( "TerrainBlock" ); + + if( %terrainExists == false ) + MessageBoxYesNoCancel("No Terrain","Would you like to create a New Terrain?", "Canvas.pushDialog(CreateNewTerrainGui);"); + + return %terrainExists; +} + +function EPainterIconBtn::onMouseDragged( %this ) +{ + %payload = new GuiControl() + { + profile = GuiCreatorIconButtonProfile; + position = "0 0"; + extent = %this.extent.x SPC "5"; + dragSourceControl = %this; + }; + + %xOffset = getWord( %payload.extent, 0 ) / 2; + %yOffset = getWord( %payload.extent, 1 ) / 2; + %cursorpos = Canvas.getCursorPos(); + %xPos = getWord( %cursorpos, 0 ) - %xOffset; + %yPos = getWord( %cursorpos, 1 ) - %yOffset; + + // Create the drag control. + + %ctrl = new GuiDragAndDropControl() + { + canSaveDynamicFields = "0"; + Profile = EPainterDragDropProfile; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = %xPos SPC %yPos; + extent = %payload.extent; + MinExtent = "4 4"; + canSave = "1"; + Visible = "1"; + hovertime = "1000"; + deleteOnMouseUp = true; + }; + + %ctrl.add( %payload ); + + Canvas.getContent().add( %ctrl ); + %ctrl.startDragging( %xOffset, %yOffset ); +} + +function EPainterIconBtn::onControlDragged( %this, %payload ) +{ + %payload.getParent().position = %this.getGlobalPosition(); +} + +function EPainterIconBtn::onControlDropped( %this, %payload ) +{ + %srcBtn = %payload.dragSourceControl; + %dstBtn = %this; + %stack = %this.getParent(); + + // Not dropped on a valid Button. + // Really this shouldnt happen since we are in a callback on our specialized + // EPainterIconBtn namespace. + if ( %stack != %dstBtn.getParent() || %stack != EPainterStack.getId() ) + { + echo( "Not dropped on valid control" ); + return; + } + + // Dropped on the original control, no order change. + // Simulate a click on the control, instead of a drag/drop. + if ( %srcBtn == %dstBtn ) + { + %dstBtn.performClick(); + return; + } + + %dstIndex = %stack.getObjectIndex( %dstBtn ); + ETerrainEditor.reorderMaterial( %stack.getObjectIndex( %srcBtn ), %dstIndex ); + + // select the button/material we just reordered. + %stack.getObject( %dstIndex ).performClick(); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/editors/worldEditor.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/worldEditor.ed.cs new file mode 100644 index 000000000..eb89d1a30 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/editors/worldEditor.ed.cs @@ -0,0 +1,486 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function WorldEditor::onSelect( %this, %obj ) +{ + EditorTree.addSelection( %obj ); + + _setShadowVizLight( %obj ); + + //Inspector.inspect( %obj ); + + if ( isObject( %obj ) && %obj.isMethod( "onEditorSelect" ) ) + %obj.onEditorSelect( %this.getSelectionSize() ); + + EditorGui.currentEditor.onObjectSelected( %obj ); + + // Inform the camera + commandToServer('EditorOrbitCameraSelectChange', %this.getSelectionSize(), %this.getSelectionCentroid()); + + EditorGuiStatusBar.setSelectionObjectsByCount(%this.getSelectionSize()); + + // Update the materialEditorList + $Tools::materialEditorList = %obj.getId(); + + // Used to help the Material Editor( the M.E doesn't utilize its own TS control ) + // so this dirty extension is used to fake it + if ( MaterialEditorPreviewWindow.isVisible() ) + MaterialEditorGui.prepareActiveObject(); + + // Update the Transform Selection window + ETransformSelection.onSelectionChanged(); +} + +function WorldEditor::onMultiSelect( %this, %set ) +{ + // This is called when completing a drag selection ( on3DMouseUp ) + // so we can avoid calling onSelect for every object. We can only + // do most of this stuff, like inspecting, on one object at a time anyway. + + %count = %set.getCount(); + %i = 0; + + foreach( %obj in %set ) + { + if ( %obj.isMethod( "onEditorSelect" ) ) + %obj.onEditorSelect( %count ); + + %i ++; + EditorTree.addSelection( %obj, %i == %count ); + EditorGui.currentEditor.onObjectSelected( %obj ); + } + + // Inform the camera + commandToServer( 'EditorOrbitCameraSelectChange', %count, %this.getSelectionCentroid() ); + + EditorGuiStatusBar.setSelectionObjectsByCount( EWorldEditor.getSelectionSize() ); + + // Update the Transform Selection window, if it is + // visible. + + if( ETransformSelection.isVisible() ) + ETransformSelection.onSelectionChanged(); +} + +function WorldEditor::onUnSelect( %this, %obj ) +{ + if ( isObject( %obj ) && %obj.isMethod( "onEditorUnselect" ) ) + %obj.onEditorUnselect(); + + EditorGui.currentEditor.onObjectDeselected( %obj ); + + Inspector.removeInspect( %obj ); + EditorTree.removeSelection(%obj); + + // Inform the camera + commandToServer('EditorOrbitCameraSelectChange', %this.getSelectionSize(), %this.getSelectionCentroid()); + + EditorGuiStatusBar.setSelectionObjectsByCount(%this.getSelectionSize()); + + // Update the Transform Selection window + ETransformSelection.onSelectionChanged(); +} + +function WorldEditor::onClearSelection( %this ) +{ + EditorGui.currentEditor.onSelectionCleared(); + + EditorTree.clearSelection(); + + // Inform the camera + commandToServer('EditorOrbitCameraSelectChange', %this.getSelectionSize(), %this.getSelectionCentroid()); + + EditorGuiStatusBar.setSelectionObjectsByCount(%this.getSelectionSize()); + + // Update the Transform Selection window + ETransformSelection.onSelectionChanged(); +} + +function WorldEditor::onSelectionCentroidChanged( %this ) +{ + // Inform the camera + commandToServer('EditorOrbitCameraSelectChange', %this.getSelectionSize(), %this.getSelectionCentroid()); + + // Refresh inspector. + Inspector.refresh(); +} + +////////////////////////////////////////////////////////////////////////// + +function WorldEditor::init(%this) +{ + // add objclasses which we do not want to collide with + %this.ignoreObjClass(Sky); + + // editing modes + %this.numEditModes = 3; + %this.editMode[0] = "move"; + %this.editMode[1] = "rotate"; + %this.editMode[2] = "scale"; + + // context menu + new GuiControl(WEContextPopupDlg, EditorGuiGroup) + { + profile = "ToolsGuiModelessDialogProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + + new GuiPopUpMenuCtrl(WEContextPopup) + { + profile = "ToolsGuiScrollProfile"; + position = "0 0"; + extent = "0 0"; + minExtent = "0 0"; + maxPopupHeight = "200"; + command = "canvas.popDialog(WEContextPopupDlg);"; + }; + }; + WEContextPopup.setVisible(false); + + // Make sure we have an active selection set. + if( !%this.getActiveSelection() ) + %this.setActiveSelection( new WorldEditorSelection( EWorldEditorSelection ) ); +} + +//------------------------------------------------------------------------------ + +function WorldEditor::onDblClick(%this, %obj) +{ + // Commented out because making someone double click to do this is stupid + // and has the possibility of moving hte object + + //Inspector.inspect(%obj); + //InspectorNameEdit.setValue(%obj.getName()); +} + +function WorldEditor::onClick( %this, %obj ) +{ + Inspector.inspect( %obj ); +} + +function WorldEditor::onEndDrag( %this, %obj ) +{ + Inspector.inspect( %obj ); + Inspector.apply(); +} + +//------------------------------------------------------------------------------ + +function WorldEditor::export(%this) +{ + getSaveFilename("~/editor/*.mac|mac file", %this @ ".doExport", "selection.mac"); +} + +function WorldEditor::doExport(%this, %file) +{ + missionGroup.save("~/editor/" @ %file, true); +} + +function WorldEditor::import(%this) +{ + getLoadFilename("~/editor/*.mac|mac file", %this @ ".doImport"); +} + +function WorldEditor::doImport(%this, %file) +{ + exec("~/editor/" @ %file); +} + +function WorldEditor::onGuiUpdate(%this, %text) +{ +} + +function WorldEditor::getSelectionLockCount(%this) +{ + %ret = 0; + for(%i = 0; %i < %this.getSelectionSize(); %i++) + { + %obj = %this.getSelectedObject(%i); + if(%obj.locked) + %ret++; + } + return %ret; +} + +function WorldEditor::getSelectionHiddenCount(%this) +{ + %ret = 0; + for(%i = 0; %i < %this.getSelectionSize(); %i++) + { + %obj = %this.getSelectedObject(%i); + if(%obj.hidden) + %ret++; + } + return %ret; +} + +function WorldEditor::dropCameraToSelection(%this) +{ + if(%this.getSelectionSize() == 0) + return; + + %pos = %this.getSelectionCentroid(); + %cam = LocalClientConnection.camera.getTransform(); + + // set the pnt + %cam = setWord(%cam, 0, getWord(%pos, 0)); + %cam = setWord(%cam, 1, getWord(%pos, 1)); + %cam = setWord(%cam, 2, getWord(%pos, 2)); + + LocalClientConnection.camera.setTransform(%cam); +} + +/// Pastes the selection at the same place (used to move obj from a group to another) +function WorldEditor::moveSelectionInPlace(%this) +{ + %saveDropType = %this.dropType; + %this.dropType = "atCentroid"; + %this.copySelection(); + %this.deleteSelection(); + %this.pasteSelection(); + %this.dropType = %saveDropType; +} + +function WorldEditor::addSelectionToAddGroup(%this) +{ + for(%i = 0; %i < %this.getSelectionSize(); %i++) + { + %obj = %this.getSelectedObject(%i); + $InstantGroup.add(%obj); + } +} + +// resets the scale and rotation on the selection set +function WorldEditor::resetTransforms(%this) +{ + %this.addUndoState(); + + for(%i = 0; %i < %this.getSelectionSize(); %i++) + { + %obj = %this.getSelectedObject(%i); + %transform = %obj.getTransform(); + + %transform = setWord(%transform, 3, "0"); + %transform = setWord(%transform, 4, "0"); + %transform = setWord(%transform, 5, "1"); + %transform = setWord(%transform, 6, "0"); + + // + %obj.setTransform(%transform); + %obj.setScale("1 1 1"); + } +} + + +function WorldEditorToolbarDlg::init(%this) +{ + WorldEditorInspectorCheckBox.setValue( WorldEditorToolFrameSet.isMember( "EditorToolInspectorGui" ) ); + WorldEditorMissionAreaCheckBox.setValue( WorldEditorToolFrameSet.isMember( "EditorToolMissionAreaGui" ) ); + WorldEditorTreeCheckBox.setValue( WorldEditorToolFrameSet.isMember( "EditorToolTreeViewGui" ) ); + WorldEditorCreatorCheckBox.setValue( WorldEditorToolFrameSet.isMember( "EditorToolCreatorGui" ) ); +} + +function WorldEditor::onAddSelected(%this,%obj) +{ + EditorTree.addSelection(%obj); +} + +function WorldEditor::onWorldEditorUndo( %this ) +{ + Inspector.refresh(); +} + +function Inspector::onInspectorFieldModified( %this, %object, %fieldName, %arrayIndex, %oldValue, %newValue ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %action = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %oldValue; + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + // If it's a datablock, initiate a retransmit. Don't do so + // immediately so as the actual field value will only be set + // by the inspector code after this method has returned. + + if( %object.isMemberOfClass( "SimDataBlock" ) ) + %object.schedule( 1, "reloadOnLocalClient" ); + + // Restore the instant group. + popInstantGroup(); + + %action.addToManager( Editor.getUndoManager() ); + EWorldEditor.isDirty = true; + + // Update the selection + if(EWorldEditor.getSelectionSize() > 0 && (%fieldName $= "position" || %fieldName $= "rotation" || %fieldName $= "scale")) + { + EWorldEditor.invalidateSelectionCentroid(); + } +} + +function Inspector::onFieldSelected( %this, %fieldName, %fieldTypeStr, %fieldDoc ) +{ + FieldInfoControl.setText( "<font:ArialBold:14>" @ %fieldName @ "<font:ArialItalic:14> (" @ %fieldTypeStr @ ") " NL "<font:Arial:14>" @ %fieldDoc ); +} + +// The following three methods are for fields that edit field value live and thus cannot record +// undo information during edits. For these fields, undo information is recorded in advance and +// then either queued or disarded when the field edit is finished. + +function Inspector::onInspectorPreFieldModification( %this, %fieldName, %arrayIndex ) +{ + pushInstantGroup(); + %undoManager = Editor.getUndoManager(); + + %numObjects = %this.getNumInspectObjects(); + if( %numObjects > 1 ) + %action = %undoManager.pushCompound( "Multiple Field Edit" ); + + for( %i = 0; %i < %numObjects; %i ++ ) + { + %object = %this.getInspectObject( %i ); + + %nameOrClass = %object.getName(); + if ( %nameOrClass $= "" ) + %nameOrClass = %object.getClassname(); + + %undo = new InspectorFieldUndoAction() + { + actionName = %nameOrClass @ "." @ %fieldName @ " Change"; + + objectId = %object.getId(); + fieldName = %fieldName; + fieldValue = %object.getFieldValue( %fieldName, %arrayIndex ); + arrayIndex = %arrayIndex; + + inspectorGui = %this; + }; + + if( %numObjects > 1 ) + %undo.addToManager( %undoManager ); + else + { + %action = %undo; + break; + } + } + + %this.currentFieldEditAction = %action; + popInstantGroup(); +} + +function Inspector::onInspectorPostFieldModification( %this ) +{ + if( %this.currentFieldEditAction.isMemberOfClass( "CompoundUndoAction" ) ) + { + // Finish multiple field edit. + Editor.getUndoManager().popCompound(); + } + else + { + // Queue single field undo. + %this.currentFieldEditAction.addToManager( Editor.getUndoManager() ); + } + + %this.currentFieldEditAction = ""; + EWorldEditor.isDirty = true; +} + +function Inspector::onInspectorDiscardFieldModification( %this ) +{ + %this.currentFieldEditAction.undo(); + + if( %this.currentFieldEditAction.isMemberOfClass( "CompoundUndoAction" ) ) + { + // Multiple field editor. Pop and discard. + Editor.getUndoManager().popCompound( true ); + } + else + { + // Single field edit. Just kill undo action. + %this.currentFieldEditAction.delete(); + } + + %this.currentFieldEditAction = ""; +} + +function Inspector::inspect( %this, %obj ) +{ + //echo( "inspecting: " @ %obj ); + + %name = ""; + if ( isObject( %obj ) ) + %name = %obj.getName(); + else + FieldInfoControl.setText( "" ); + + //InspectorNameEdit.setValue( %name ); + Parent::inspect( %this, %obj ); +} + +function Inspector::onBeginCompoundEdit( %this ) +{ + Editor.getUndoManager().pushCompound( "Multiple Field Edit" ); +} + +function Inspector::onEndCompoundEdit( %this ) +{ + Editor.getUndoManager().popCompound(); +} + +function Inspector::onCancelCompoundEdit( %this ) +{ + Editor.getUndoManager().popCompound( true ); +} + +function foCollaps (%this, %tab){ + switch$ (%tab){ + case "container0": + %tab.visible = "0"; + buttxon1.position = getWord(buttxon0.position, 0)+32 SPC getWord(buttxon1.position, 1); + buttxon2.position = getWord(buttxon1.position, 0)+32 SPC getWord(buttxon2.position, 1); + case "container1": + %tab.visible = "0"; + case "container2": + %tab.visible = "0"; + } +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/levelInfoEditor.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/levelInfoEditor.ed.cs new file mode 100644 index 000000000..903f14dcf --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/levelInfoEditor.ed.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +// Define the field types for objects that link to the namespace LevelInfo +function LevelInfo::onDefineFieldTypes( %this ) +{ + %this.setFieldType("Desc", "TypeString"); + %this.setFieldType("DescLines", "TypeS32"); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/simObjectEditor.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/simObjectEditor.ed.cs new file mode 100644 index 000000000..57826ae77 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/simObjectEditor.ed.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- +// Define the field types for objects that link to the namespace MissionInfo +function SimObject::onDefineFieldTypes( %this ) +{ + %this.setFieldType("Locked", "TypeBool"); +} \ No newline at end of file diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs new file mode 100644 index 000000000..2ec8e17f3 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs @@ -0,0 +1,625 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::show( %this, %matIndex, %terrMat, %onApplyCallback ) +{ + Canvas.pushDialog( %this ); + + %this.matIndex = %matIndex; + %this.onApplyCallback = %onApplyCallback; + + %matLibTree = %this-->matLibTree; + %item = %matLibTree.findItemByObjectId( %terrMat ); + if ( %item != -1 ) + { + %matLibTree.selectItem( %item ); + %matLibTree.scrollVisible( %item ); + } + else + { + for( %i = 1; %i < %matLibTree.getItemCount(); %i++ ) + { + %terrMat = TerrainMaterialDlg-->matLibTree.getItemValue(%i); + if( %terrMat.getClassName() $= "TerrainMaterial" ) + { + %matLibTree.selectItem( %i, true ); + %matLibTree.scrollVisible( %i ); + break; + } + } + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::showByObjectId( %this, %matObjectId, %onApplyCallback ) +{ + Canvas.pushDialog( %this ); + + %this.matIndex = -1; + %this.onApplyCallback = %onApplyCallback; + + %matLibTree = %this-->matLibTree; + %matLibTree.clearSelection(); + %item = %matLibTree.findItemByObjectId( %matObjectId ); + if ( %item != -1 ) + { + %matLibTree.selectItem( %item ); + %matLibTree.scrollVisible( %item ); + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::onWake( %this ) +{ + if( !isObject( ETerrainMaterialPersistMan ) ) + new PersistenceManager( ETerrainMaterialPersistMan ); + + if( !isObject( TerrainMaterialDlgNewGroup ) ) + new SimGroup( TerrainMaterialDlgNewGroup ); + if( !isObject( TerrainMaterialDlgDeleteGroup ) ) + new SimGroup( TerrainMaterialDlgDeleteGroup ); + + // Snapshot the materials. + %this.snapshotMaterials(); + + // Refresh the material list. + %matLibTree = %this-->matLibTree; + %matLibTree.clear(); + + %matLibTree.open( TerrainMaterialSet, false ); + + %matLibTree.buildVisibleTree( true ); + %item = %matLibTree.getFirstRootItem(); + %matLibTree.expandItem( %item ); + + %this.activateMaterialCtrls( true ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::onSleep( %this ) +{ + if( isObject( TerrainMaterialDlgSnapshot ) ) + TerrainMaterialDlgSnapshot.delete(); + + %this-->matLibTree.clear(); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::dialogApply( %this ) +{ + // Move all new materials we have created to the root group. + + %newCount = TerrainMaterialDlgNewGroup.getCount(); + for( %i = 0; %i < %newCount; %i ++ ) + RootGroup.add( TerrainMaterialDlgNewGroup.getObject( %i ) ); + + // Finalize deletion of all removed materials. + + %deletedCount = TerrainMaterialDlgDeleteGroup.getCount(); + for( %i = 0; %i < %deletedCount; %i ++ ) + { + %mat = TerrainMaterialDlgDeleteGroup.getObject( %i ); + ETerrainMaterialPersistMan.removeObjectFromFile( %mat ); + + %matIndex = ETerrainEditor.getMaterialIndex( %mat.internalName ); + if( %matIndex != -1 ) + { + ETerrainEditor.removeMaterial( %matIndex ); + EPainter.updateLayers(); + } + + %mat.delete(); + } + + // Make sure we save any changes to the current selection. + %this.saveDirtyMaterial( %this.activeMat ); + + // Save all changes. + ETerrainMaterialPersistMan.saveDirty(); + + // Delete the snapshot. + TerrainMaterialDlgSnapshot.delete(); + + // Remove ourselves from the canvas. + Canvas.popDialog( TerrainMaterialDlg ); + + call( %this.onApplyCallback, %this.activeMat, %this.matIndex ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::dialogCancel( %this ) +{ + // Restore material properties we have changed. + + %this.restoreMaterials(); + + // Clear the persistence manager state. + + ETerrainMaterialPersistMan.clearAll(); + + // Delete all new object we have created. + + TerrainMaterialDlgNewGroup.clear(); + + // Restore materials we have marked for deletion. + + %deletedCount = TerrainMaterialDlgDeleteGroup.getCount(); + for( %i = 0; %i < %deletedCount; %i ++ ) + { + %mat = TerrainMaterialDlgDeleteGroup.getObject( %i ); + %mat.parentGroup = RootGroup; + TerrainMaterialSet.add( %mat ); + } + + Canvas.popDialog( TerrainMaterialDlg ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::setMaterialName( %this, %newName ) +{ + %mat = %this.activeMat; + + if( %mat.internalName !$= %newName ) + { + %existingMat = TerrainMaterialSet.findObjectByInternalName( %newName ); + if( isObject( %existingMat ) ) + { + MessageBoxOK( "Error", + "There already is a terrain material called '" @ %newName @ "'.", "", "" ); + } + else + { + %mat.setInternalName( %newName ); + %this-->matLibTree.buildVisibleTree( false ); + } + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::changeBase( %this ) +{ + %ctrl = %this-->baseTexCtrl; + %file = %ctrl.bitmap; + if( getSubStr( %file, 0 , 6 ) $= "tools/" ) + %file = ""; + + %file = TerrainMaterialDlg._selectTextureFileDialog( %file ); + if( %file $= "" ) + { + if( %ctrl.bitmap !$= "" ) + %file = %ctrl.bitmap; + else + %file = "tools/materialEditor/gui/unknownImage"; + } + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %ctrl.setBitmap( %file ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::changeDetail( %this ) +{ + %ctrl = %this-->detailTexCtrl; + %file = %ctrl.bitmap; + if( getSubStr( %file, 0 , 6 ) $= "tools/" ) + %file = ""; + + %file = TerrainMaterialDlg._selectTextureFileDialog( %file ); + if( %file $= "" ) + { + if( %ctrl.bitmap !$= "" ) + %file = %ctrl.bitmap; + else + %file = "tools/materialEditor/gui/unknownImage"; + } + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %ctrl.setBitmap( %file ); +} + +//---------------------------------------------------------------------------- + +function TerrainMaterialDlg::changeMacro( %this ) +{ + %ctrl = %this-->macroTexCtrl; + %file = %ctrl.bitmap; + if( getSubStr( %file, 0 , 6 ) $= "tools/" ) + %file = ""; + + %file = TerrainMaterialDlg._selectTextureFileDialog( %file ); + if( %file $= "" ) + { + if( %ctrl.bitmap !$= "" ) + %file = %ctrl.bitmap; + else + %file = "tools/materialEditor/gui/unknownImage"; + } + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %ctrl.setBitmap( %file ); +} + + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::changeNormal( %this ) +{ + %ctrl = %this-->normTexCtrl; + %file = %ctrl.bitmap; + if( getSubStr( %file, 0 , 6 ) $= "tools/" ) + %file = ""; + + %file = TerrainMaterialDlg._selectTextureFileDialog( %file ); + if( %file $= "" ) + { + if( %ctrl.bitmap !$= "" ) + %file = %ctrl.bitmap; + else + %file = "tools/materialEditor/gui/unknownImage"; + } + + %file = makeRelativePath( %file, getMainDotCsDir() ); + %ctrl.setBitmap( %file ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::newMat( %this ) +{ + // Create a unique material name. + %matName = getUniqueInternalName( "newMaterial", TerrainMaterialSet, true ); + + // Create the new material. + %newMat = new TerrainMaterial() + { + internalName = %matName; + parentGroup = TerrainMaterialDlgNewGroup; + }; + %newMat.setFileName( "art/terrains/materials.cs" ); + + // Mark it as dirty and to be saved in the default location. + ETerrainMaterialPersistMan.setDirty( %newMat, "art/terrains/materials.cs" ); + + %matLibTree = %this-->matLibTree; + %matLibTree.buildVisibleTree( true ); + %item = %matLibTree.findItemByObjectId( %newMat ); + %matLibTree.selectItem( %item ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::deleteMat( %this ) +{ + if( !isObject( %this.activeMat ) ) + return; + + // Cannot delete this material if it is the only one left on the Terrain + if ( ( ETerrainEditor.getMaterialCount() == 1 ) && + ( ETerrainEditor.getMaterialIndex( %this.activeMat.internalName ) != -1 ) ) + { + MessageBoxOK( "Error", "Cannot delete this Material, it is the only " @ + "Material still in use by the active Terrain." ); + return; + } + + TerrainMaterialSet.remove( %this.activeMat ); + TerrainMaterialDlgDeleteGroup.add( %this.activeMat ); + + %matLibTree = %this-->matLibTree; + %matLibTree.open( TerrainMaterialSet, false ); + %matLibTree.selectItem( 1 ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::activateMaterialCtrls( %this, %active ) +{ + %parent = %this-->matSettingsParent; + %count = %parent.getCount(); + for ( %i = 0; %i < %count; %i++ ) + %parent.getObject( %i ).setActive( %active ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialTreeCtrl::onSelect( %this, %item ) +{ + TerrainMaterialDlg.setActiveMaterial( %item ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialTreeCtrl::onUnSelect( %this, %item ) +{ + TerrainMaterialDlg.saveDirtyMaterial( %item ); + TerrainMaterialDlg.setActiveMaterial( 0 ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::setActiveMaterial( %this, %mat ) +{ + if ( isObject( %mat ) && + %mat.isMemberOfClass( TerrainMaterial ) ) + { + %this.activeMat = %mat; + + %this-->matNameCtrl.setText( %mat.internalName ); + if (%mat.diffuseMap $= ""){ + %this-->baseTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" ); + }else{ + %this-->baseTexCtrl.setBitmap( %mat.diffuseMap ); + } + if (%mat.detailMap $= ""){ + %this-->detailTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" ); + }else{ + %this-->detailTexCtrl.setBitmap( %mat.detailMap ); + } + if (%mat.macroMap $= ""){ + %this-->macroTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" ); + }else{ + %this-->macroTexCtrl.setBitmap( %mat.macroMap ); + } + if (%mat.normalMap $= ""){ + %this-->normTexCtrl.setBitmap( "tools/materialEditor/gui/unknownImage" ); + }else{ + %this-->normTexCtrl.setBitmap( %mat.normalMap ); + } + %this-->detSizeCtrl.setText( %mat.detailSize ); + %this-->baseSizeCtrl.setText( %mat.diffuseSize ); + %this-->detStrengthCtrl.setText( %mat.detailStrength ); + %this-->detDistanceCtrl.setText( %mat.detailDistance ); + %this-->sideProjectionCtrl.setValue( %mat.useSideProjection ); + %this-->parallaxScaleCtrl.setText( %mat.parallaxScale ); + + %this-->macroSizeCtrl.setText( %mat.macroSize ); + %this-->macroStrengthCtrl.setText( %mat.macroStrength ); + %this-->macroDistanceCtrl.setText( %mat.macroDistance ); + + %this.activateMaterialCtrls( true ); + } + else + { + %this.activeMat = 0; + %this.activateMaterialCtrls( false ); + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat ) +{ + // Skip over obviously bad cases. + if ( !isObject( %mat ) || + !%mat.isMemberOfClass( TerrainMaterial ) ) + return; + + // Read out properties from the dialog. + + %newName = %this-->matNameCtrl.getText(); + + if (%this-->baseTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){ + %newDiffuse = ""; + }else{ + %newDiffuse = %this-->baseTexCtrl.bitmap; + } + if (%this-->normTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){ + %newNormal = ""; + }else{ + %newNormal = %this-->normTexCtrl.bitmap; + } + if (%this-->detailTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){ + %newDetail = ""; + }else{ + %newDetail = %this-->detailTexCtrl.bitmap; + } + if (%this-->macroTexCtrl.bitmap $= "tools/materialEditor/gui/unknownImage"){ + %newMacro = ""; + }else{ + %newMacro = %this-->macroTexCtrl.bitmap; + } + %detailSize = %this-->detSizeCtrl.getText(); + %diffuseSize = %this-->baseSizeCtrl.getText(); + %detailStrength = %this-->detStrengthCtrl.getText(); + %detailDistance = %this-->detDistanceCtrl.getText(); + %useSideProjection = %this-->sideProjectionCtrl.getValue(); + %parallaxScale = %this-->parallaxScaleCtrl.getText(); + + %macroSize = %this-->macroSizeCtrl.getText(); + %macroStrength = %this-->macroStrengthCtrl.getText(); + %macroDistance = %this-->macroDistanceCtrl.getText(); + + // If no properties of this materials have changed, + // return. + + if ( %mat.internalName $= %newName && + %mat.diffuseMap $= %newDiffuse && + %mat.normalMap $= %newNormal && + %mat.detailMap $= %newDetail && + %mat.macroMap $= %newMacro && + %mat.detailSize == %detailSize && + %mat.diffuseSize == %diffuseSize && + %mat.detailStrength == %detailStrength && + %mat.detailDistance == %detailDistance && + %mat.useSideProjection == %useSideProjection && + %mat.macroSize == %macroSize && + %mat.macroStrength == %macroStrength && + %mat.macroDistance == %macroDistance && + %mat.parallaxScale == %parallaxScale ) + return; + + // Make sure the material name is unique. + + if( %mat.internalName !$= %newName ) + { + %existingMat = TerrainMaterialSet.findObjectByInternalName( %newName ); + if( isObject( %existingMat ) ) + { + MessageBoxOK( "Error", + "There already is a terrain material called '" @ %newName @ "'.", "", "" ); + + // Reset the name edit control to the old name. + + %this-->matNameCtrl.setText( %mat.internalName ); + } + else + %mat.setInternalName( %newName ); + } + + %mat.diffuseMap = %newDiffuse; + %mat.normalMap = %newNormal; + %mat.detailMap = %newDetail; + %mat.macroMap = %newMacro; + %mat.detailSize = %detailSize; + %mat.diffuseSize = %diffuseSize; + %mat.detailStrength = %detailStrength; + %mat.detailDistance = %detailDistance; + %mat.macroSize = %macroSize; + %mat.macroStrength = %macroStrength; + %mat.macroDistance = %macroDistance; + %mat.useSideProjection = %useSideProjection; + %mat.parallaxScale = %parallaxScale; + + // Mark the material as dirty and needing saving. + + %fileName = %mat.getFileName(); + if( %fileName $= "" ) + %fileName = "art/terrains/materials.cs"; + + ETerrainMaterialPersistMan.setDirty( %mat, %fileName ); +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::snapshotMaterials( %this ) +{ + if( !isObject( TerrainMaterialDlgSnapshot ) ) + new SimGroup( TerrainMaterialDlgSnapshot ); + + %group = TerrainMaterialDlgSnapshot; + %group.clear(); + + %matCount = TerrainMaterialSet.getCount(); + for( %i = 0; %i < %matCount; %i ++ ) + { + %mat = TerrainMaterialSet.getObject( %i ); + if( !isMemberOfClass( %mat.getClassName(), "TerrainMaterial" ) ) + continue; + + %snapshot = new ScriptObject() + { + parentGroup = %group; + material = %mat; + internalName = %mat.internalName; + diffuseMap = %mat.diffuseMap; + normalMap = %mat.normalMap; + detailMap = %mat.detailMap; + macroMap = %mat.macroMap; + detailSize = %mat.detailSize; + diffuseSize = %mat.diffuseSize; + detailStrength = %mat.detailStrength; + detailDistance = %mat.detailDistance; + macroSize = %mat.macroSize; + macroStrength = %mat.macroStrength; + macroDistance = %mat.macroDistance; + useSideProjection = %mat.useSideProjection; + parallaxScale = %mat.parallaxScale; + }; + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::restoreMaterials( %this ) +{ + if( !isObject( TerrainMaterialDlgSnapshot ) ) + { + error( "TerrainMaterial::restoreMaterials - no snapshot present" ); + return; + } + + %count = TerrainMaterialDlgSnapshot.getCount(); + for( %i = 0; %i < %count; %i ++ ) + { + %obj = TerrainMaterialDlgSnapshot.getObject( %i ); + %mat = %obj.material; + + %mat.setInternalName( %obj.internalName ); + %mat.diffuseMap = %obj.diffuseMap; + %mat.normalMap = %obj.normalMap; + %mat.detailMap = %obj.detailMap; + %mat.macroMap = %obj.macroMap; + %mat.detailSize = %obj.detailSize; + %mat.diffuseSize = %obj.diffuseSize; + %mat.detailStrength = %obj.detailStrength; + %mat.detailDistance = %obj.detailDistance; + %mat.macroSize = %obj.macroSize; + %mat.macroStrength = %obj.macroStrength; + %mat.macroDistance = %obj.macroDistance; + %mat.useSideProjection = %obj.useSideProjection; + %mat.parallaxScale = %obj.parallaxScale; + } +} + +//----------------------------------------------------------------------------- + +function TerrainMaterialDlg::_selectTextureFileDialog( %this, %defaultFileName ) +{ + if( $Pref::TerrainEditor::LastPath $= "" ) + $Pref::TerrainEditor::LastPath = "art/terrains"; + + %dlg = new OpenFileDialog() + { + Filters = $TerrainEditor::TextureFileSpec; + DefaultPath = $Pref::TerrainEditor::LastPath; + DefaultFile = %defaultFileName; + ChangePath = false; + MustExist = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::TerrainEditor::LastPath = filePath( %dlg.FileName ); + %file = %dlg.FileName; + } + + %dlg.delete(); + + if ( !%ret ) + return; + + %file = filePath(%file) @ "/" @ fileBase(%file); + + return %file; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/lighting.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/lighting.ed.cs new file mode 100644 index 000000000..8886b8eb9 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/lighting.ed.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EditorLightingMenu::onAdd( %this ) +{ + Parent::onAdd( %this ); + + // Get the light manager names. + %lightManagers = getLightManagerNames(); + + // Where we gonna insert them? + %this.lmFirstIndex = %this.getItemCount(); + + // Add the light mangers to the lighting menu. + %count = getFieldCount( %lightManagers ); + for ( %i = 0; %i < %count; %i++ ) + { + %lm = getField( %lightManagers, %i ); + + // Store a reverse lookup of the light manager + // name to the menu index... used in onMenuSelect. + %index = %this.lmFirstIndex + %i; + %this.lmToIndex[ %lm ] = %index; + + // The command just sets the light manager. + %cmd = "setLightManager(\"" @ %lm @ "\"); $pref::lightManager = \"" @ %lm @ "\";"; + + // Add it. + %this.addItem( %index, %lm TAB "" TAB %cmd ); + } + + // Store for later in EditorLightingMenu. + %this.lmLastIndex = %index; +} + +function EditorLightingMenu::onMenuSelect( %this ) +{ + %lm = getActiveLightManager(); + %index = %this.lmToIndex[ %lm ]; + %this.checkRadioItem( %this.lmFirstIndex, %this.lmLastIndex, %index ); + + //%selSize = EWorldEditor.getSelectionSize(); + %this.enableItem( 1, true /*%selSize == 1*/ ); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.cs new file mode 100644 index 000000000..f476ccaeb --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.cs @@ -0,0 +1,906 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +$Pref::WorldEditor::FileSpec = "Torque Mission Files (*.mis)|*.mis|All Files (*.*)|*.*|"; + +////////////////////////////////////////////////////////////////////////// +// File Menu Handlers +////////////////////////////////////////////////////////////////////////// + +function EditorFileMenu::onMenuSelect(%this) +{ + %this.enableItem(2, EditorIsDirty()); +} + +////////////////////////////////////////////////////////////////////////// + +// Package that gets temporarily activated to toggle editor after mission loading. +// Deactivates itself. +package BootEditor { + +function GameConnection::initialControlSet( %this ) +{ + Parent::initialControlSet( %this ); + + toggleEditor( true ); + deactivatePackage( "BootEditor" ); +} + +}; + +////////////////////////////////////////////////////////////////////////// + +/// Checks the various dirty flags and returns true if the +/// mission or other related resources need to be saved. +function EditorIsDirty() +{ + // We kept a hard coded test here, but we could break these + // into the registered tools if we wanted to. + %isDirty = ( isObject( "ETerrainEditor" ) && ( ETerrainEditor.isMissionDirty || ETerrainEditor.isDirty ) ) + || ( isObject( "EWorldEditor" ) && EWorldEditor.isDirty ) + || ( isObject( "ETerrainPersistMan" ) && ETerrainPersistMan.hasDirty() ); + + // Give the editor plugins a chance to set the dirty flag. + for ( %i = 0; %i < EditorPluginSet.getCount(); %i++ ) + { + %obj = EditorPluginSet.getObject(%i); + %isDirty |= %obj.isDirty(); + } + + return %isDirty; +} + +/// Clears all the dirty state without saving. +function EditorClearDirty() +{ + EWorldEditor.isDirty = false; + ETerrainEditor.isDirty = false; + ETerrainEditor.isMissionDirty = false; + ETerrainPersistMan.clearAll(); + + for ( %i = 0; %i < EditorPluginSet.getCount(); %i++ ) + { + %obj = EditorPluginSet.getObject(%i); + %obj.clearDirty(); + } +} + +function EditorQuitGame() +{ + if( EditorIsDirty()) + { + MessageBoxYesNoCancel("Level Modified", "Would you like to save your changes before quitting?", "EditorSaveMissionMenu(); quit();", "quit();", "" ); + } + else + quit(); +} + +function EditorExitMission() +{ + if( EditorIsDirty()) + { + MessageBoxYesNoCancel("Level Modified", "Would you like to save your changes before exiting?", "EditorDoExitMission(true);", "EditorDoExitMission(false);", ""); + } + else + EditorDoExitMission(false); +} + +function EditorDoExitMission(%saveFirst) +{ + if(%saveFirst) + { + EditorSaveMissionMenu(); + } + else + { + EditorClearDirty(); + } + + if (isObject( MainMenuGui )) + Editor.close("MainMenuGui"); + + disconnect(); +} + +function EditorOpenTorsionProject( %projectFile ) +{ + // Make sure we have a valid path to the Torsion installation. + + %torsionPath = EditorSettings.value( "WorldEditor/torsionPath" ); + if( !isFile( %torsionPath ) ) + { + MessageBoxOK( + "Torsion Not Found", + "Torsion not found at '" @ %torsionPath @ "'. Please set the correct path in the preferences." + ); + return; + } + + // Determine the path to the .torsion file. + + if( %projectFile $= "" ) + { + %projectName = fileBase( getExecutableName() ); + %projectFile = makeFullPath( %projectName @ ".torsion" ); + if( !isFile( %projectFile ) ) + { + %projectFile = findFirstFile( "*.torsion", false ); + if( !isFile( %projectFile ) ) + { + MessageBoxOK( + "Project File Not Found", + "Cannot find .torsion project file in '" @ getMainDotCsDir() @ "'." + ); + return; + } + } + } + + // Open the project in Torsion. + + shellExecute( %torsionPath, "\"" @ %projectFile @ "\"" ); +} + +function EditorOpenFileInTorsion( %file, %line ) +{ + // Make sure we have a valid path to the Torsion installation. + + %torsionPath = EditorSettings.value( "WorldEditor/torsionPath" ); + if( !isFile( %torsionPath ) ) + { + MessageBoxOK( + "Torsion Not Found", + "Torsion not found at '" @ %torsionPath @ "'. Please set the correct path in the preferences." + ); + return; + } + + // If no file was specified, take the current mission file. + + if( %file $= "" ) + %file = makeFullPath( $Server::MissionFile ); + + // Open the file in Torsion. + + %args = "\"" @ %file; + if( %line !$= "" ) + %args = %args @ ":" @ %line; + %args = %args @ "\""; + + shellExecute( %torsionPath, %args ); +} + +function EditorOpenDeclarationInTorsion( %object ) +{ + %fileName = %object.getFileName(); + if( %fileName $= "" ) + return; + + EditorOpenFileInTorsion( makeFullPath( %fileName ), %object.getDeclarationLine() ); +} + +function EditorNewLevel( %file ) +{ + %saveFirst = false; + if ( EditorIsDirty() ) + { + error(knob); + %saveFirst = MessageBox("Mission Modified", "Would you like to save changes to the current mission \"" @ + $Server::MissionFile @ "\" before creating a new mission?", "SaveDontSave", "Question") == $MROk; + } + + if(%saveFirst) + EditorSaveMission(); + + // Clear dirty flags first to avoid duplicate dialog box from EditorOpenMission() + if( isObject( Editor ) ) + { + EditorClearDirty(); + Editor.getUndoManager().clearAll(); + } + + if( %file $= "" ) + %file = EditorSettings.value( "WorldEditor/newLevelFile" ); + + if( !$missionRunning ) + { + activatePackage( "BootEditor" ); + StartLevel( %file ); + } + else + EditorOpenMission(%file); + + //EWorldEditor.isDirty = true; + //ETerrainEditor.isDirty = true; + EditorGui.saveAs = true; +} + +function EditorSaveMissionMenu() +{ + if(EditorGui.saveAs) + EditorSaveMissionAs(); + else + EditorSaveMission(); +} + +function EditorSaveMission() +{ + // just save the mission without renaming it + + // first check for dirty and read-only files: + if((EWorldEditor.isDirty || ETerrainEditor.isMissionDirty) && !isWriteableFileName($Server::MissionFile)) + { + MessageBox("Error", "Mission file \""@ $Server::MissionFile @ "\" is read-only. Continue?", "Ok", "Stop"); + return false; + } + if(ETerrainEditor.isDirty) + { + // Find all of the terrain files + initContainerTypeSearch($TypeMasks::TerrainObjectType); + + while ((%terrainObject = containerSearchNext()) != 0) + { + if (!isWriteableFileName(%terrainObject.terrainFile)) + { + if (MessageBox("Error", "Terrain file \""@ %terrainObject.terrainFile @ "\" is read-only. Continue?", "Ok", "Stop") == $MROk) + continue; + else + return false; + } + } + } + + // now write the terrain and mission files out: + + if(EWorldEditor.isDirty || ETerrainEditor.isMissionDirty) + MissionGroup.save($Server::MissionFile); + if(ETerrainEditor.isDirty) + { + // Find all of the terrain files + initContainerTypeSearch($TypeMasks::TerrainObjectType); + + while ((%terrainObject = containerSearchNext()) != 0) + %terrainObject.save(%terrainObject.terrainFile); + } + + ETerrainPersistMan.saveDirty(); + + // Give EditorPlugins a chance to save. + for ( %i = 0; %i < EditorPluginSet.getCount(); %i++ ) + { + %obj = EditorPluginSet.getObject(%i); + if ( %obj.isDirty() ) + %obj.onSaveMission( $Server::MissionFile ); + } + + EditorClearDirty(); + + EditorGui.saveAs = false; + + return true; +} + +function EditorSaveMissionAs( %missionName ) +{ + // If we didn't get passed a new mission name then + // prompt the user for one. + if ( %missionName $= "" ) + { + %dlg = new SaveFileDialog() + { + Filters = $Pref::WorldEditor::FileSpec; + DefaultPath = EditorSettings.value("LevelInformation/levelsDirectory"); + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if(%ret) + { + // Immediately override/set the levelsDirectory + EditorSettings.setValue( "LevelInformation/levelsDirectory", collapseFilename(filePath( %dlg.FileName )) ); + + %missionName = %dlg.FileName; + } + + %dlg.delete(); + + if(! %ret) + return; + } + + if( fileExt( %missionName ) !$= ".mis" ) + %missionName = %missionName @ ".mis"; + + EWorldEditor.isDirty = true; + %saveMissionFile = $Server::MissionFile; + + $Server::MissionFile = %missionName; + + %copyTerrainsFailed = false; + + // Rename all the terrain files. Save all previous names so we can + // reset them if saving fails. + %newMissionName = fileBase(%missionName); + %oldMissionName = fileBase(%saveMissionFile); + + initContainerTypeSearch( $TypeMasks::TerrainObjectType ); + %savedTerrNames = new ScriptObject(); + for( %i = 0;; %i ++ ) + { + %terrainObject = containerSearchNext(); + if( !%terrainObject ) + break; + + %savedTerrNames.array[ %i ] = %terrainObject.terrainFile; + + %terrainFilePath = makeRelativePath( filePath( %terrainObject.terrainFile ), getMainDotCsDir() ); + %terrainFileName = fileName( %terrainObject.terrainFile ); + + // Workaround to have terrains created in an unsaved "New Level..." mission + // moved to the correct place. + + if( EditorGui.saveAs && %terrainFilePath $= "tools/art/terrains" ) + %terrainFilePath = "art/terrains"; + + // Try and follow the existing naming convention. + // If we can't, use systematic terrain file names. + if( strstr( %terrainFileName, %oldMissionName ) >= 0 ) + %terrainFileName = strreplace( %terrainFileName, %oldMissionName, %newMissionName ); + else + %terrainFileName = %newMissionName @ "_" @ %i @ ".ter"; + + %newTerrainFile = %terrainFilePath @ "/" @ %terrainFileName; + + if (!isWriteableFileName(%newTerrainFile)) + { + if (MessageBox("Error", "Terrain file \""@ %newTerrainFile @ "\" is read-only. Continue?", "Ok", "Stop") == $MROk) + continue; + else + { + %copyTerrainsFailed = true; + break; + } + } + + if( !%terrainObject.save( %newTerrainFile ) ) + { + error( "Failed to save '" @ %newTerrainFile @ "'" ); + %copyTerrainsFailed = true; + break; + } + + %terrainObject.terrainFile = %newTerrainFile; + } + + ETerrainEditor.isDirty = false; + + // Save the mission. + if(%copyTerrainsFailed || !EditorSaveMission()) + { + // It failed, so restore the mission and terrain filenames. + + $Server::MissionFile = %saveMissionFile; + + initContainerTypeSearch( $TypeMasks::TerrainObjectType ); + for( %i = 0;; %i ++ ) + { + %terrainObject = containerSearchNext(); + if( !%terrainObject ) + break; + + %terrainObject.terrainFile = %savedTerrNames.array[ %i ]; + } + } + + %savedTerrNames.delete(); +} + +function EditorOpenMission(%filename) +{ + if( EditorIsDirty()) + { + // "EditorSaveBeforeLoad();", "getLoadFilename(\"*.mis\", \"EditorDoLoadMission\");" + if(MessageBox("Mission Modified", "Would you like to save changes to the current mission \"" @ + $Server::MissionFile @ "\" before opening a new mission?", SaveDontSave, Question) == $MROk) + { + if(! EditorSaveMission()) + return; + } + } + + if(%filename $= "") + { + %dlg = new OpenFileDialog() + { + Filters = $Pref::WorldEditor::FileSpec; + DefaultPath = EditorSettings.value("LevelInformation/levelsDirectory"); + ChangePath = false; + MustExist = true; + }; + + %ret = %dlg.Execute(); + if(%ret) + { + // Immediately override/set the levelsDirectory + EditorSettings.setValue( "LevelInformation/levelsDirectory", collapseFilename(filePath( %dlg.FileName )) ); + %filename = %dlg.FileName; + } + + %dlg.delete(); + + if(! %ret) + return; + } + + // close the current editor, it will get cleaned up by MissionCleanup + if( isObject( "Editor" ) ) + Editor.close( LoadingGui ); + + EditorClearDirty(); + + // If we haven't yet connnected, create a server now. + // Otherwise just load the mission. + + if( !$missionRunning ) + { + activatePackage( "BootEditor" ); + StartLevel( %filename ); + } + else + { + loadMission( %filename, true ) ; + + pushInstantGroup(); + + // recreate and open the editor + Editor::create(); + MissionCleanup.add( Editor ); + MissionCleanup.add( Editor.getUndoManager() ); + EditorGui.loadingMission = true; + Editor.open(); + + popInstantGroup(); + } +} + +function EditorExportToCollada() +{ + + %dlg = new SaveFileDialog() + { + Filters = "COLLADA Files (*.dae)|*.dae|"; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %exportFile = %dlg.FileName; + } + + if( fileExt( %exportFile ) !$= ".dae" ) + %exportFile = %exportFile @ ".dae"; + + %dlg.delete(); + + if ( !%ret ) + return; + + if ( EditorGui.currentEditor.getId() == ShapeEditorPlugin.getId() ) + ShapeEdShapeView.exportToCollada( %exportFile ); + else + EWorldEditor.colladaExportSelection( %exportFile ); +} + +function EditorMakePrefab() +{ + + %dlg = new SaveFileDialog() + { + Filters = "Prefab Files (*.prefab)|*.prefab|"; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %saveFile = %dlg.FileName; + } + + if( fileExt( %saveFile ) !$= ".prefab" ) + %saveFile = %saveFile @ ".prefab"; + + %dlg.delete(); + + if ( !%ret ) + return; + + EWorldEditor.makeSelectionPrefab( %saveFile ); + + EditorTree.buildVisibleTree( true ); +} + +function EditorExplodePrefab() +{ + //echo( "EditorExplodePrefab()" ); + EWorldEditor.explodeSelectedPrefab(); + EditorTree.buildVisibleTree( true ); +} + +function makeSelectedAMesh() +{ + + %dlg = new SaveFileDialog() + { + Filters = "Collada file (*.dae)|*.dae|"; + DefaultPath = $Pref::WorldEditor::LastPath; + DefaultFile = ""; + ChangePath = false; + OverwritePrompt = true; + }; + + %ret = %dlg.Execute(); + if ( %ret ) + { + $Pref::WorldEditor::LastPath = filePath( %dlg.FileName ); + %saveFile = %dlg.FileName; + } + + if( fileExt( %saveFile ) !$= ".dae" ) + %saveFile = %saveFile @ ".dae"; + + %dlg.delete(); + + if ( !%ret ) + return; + + EWorldEditor.makeSelectionAMesh( %saveFile ); + + EditorTree.buildVisibleTree( true ); +} + +function EditorMount() +{ + echo( "EditorMount" ); + + %size = EWorldEditor.getSelectionSize(); + if ( %size != 2 ) + return; + + %a = EWorldEditor.getSelectedObject(0); + %b = EWorldEditor.getSelectedObject(1); + + //%a.mountObject( %b, 0 ); + EWorldEditor.mountRelative( %a, %b ); +} + +function EditorUnmount() +{ + echo( "EditorUnmount" ); + + %obj = EWorldEditor.getSelectedObject(0); + %obj.unmount(); +} + +////////////////////////////////////////////////////////////////////////// +// View Menu Handlers +////////////////////////////////////////////////////////////////////////// + +function EditorViewMenu::onMenuSelect( %this ) +{ + %this.checkItem( 1, EWorldEditor.renderOrthoGrid ); +} + +////////////////////////////////////////////////////////////////////////// +// Edit Menu Handlers +////////////////////////////////////////////////////////////////////////// + +function EditorEditMenu::onMenuSelect( %this ) +{ + // UndoManager is in charge of enabling or disabling the undo/redo items. + Editor.getUndoManager().updateUndoMenu( %this ); + + // SICKHEAD: It a perfect world we would abstract + // cut/copy/paste with a generic selection object + // which would know how to process itself. + + // Give the active editor a chance at fixing up + // the state of the edit menu. + // Do we really need this check here? + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.onEditMenuSelect( %this ); +} + +////////////////////////////////////////////////////////////////////////// + +function EditorMenuEditDelete() +{ + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.handleDelete(); +} + +function EditorMenuEditDeselect() +{ + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.handleDeselect(); +} + +function EditorMenuEditCut() +{ + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.handleCut(); +} + +function EditorMenuEditCopy() +{ + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.handleCopy(); +} + +function EditorMenuEditPaste() +{ + if ( isObject( EditorGui.currentEditor ) ) + EditorGui.currentEditor.handlePaste(); +} + + + +////////////////////////////////////////////////////////////////////////// +// Window Menu Handler +////////////////////////////////////////////////////////////////////////// + +function EditorToolsMenu::onSelectItem(%this, %id) +{ + %toolName = getField( %this.item[%id], 2 ); + + EditorGui.setEditor(%toolName, %paletteName ); + + %this.checkRadioItem(0, %this.getItemCount(), %id); + return true; +} + +function EditorToolsMenu::setupDefaultState(%this) +{ + Parent::setupDefaultState(%this); +} + +////////////////////////////////////////////////////////////////////////// +// Camera Menu Handler +////////////////////////////////////////////////////////////////////////// + +function EditorCameraMenu::onSelectItem(%this, %id, %text) +{ + if(%id == 0 || %id == 1) + { + // Handle the Free Camera/Orbit Camera toggle + %this.checkRadioItem(0, 1, %id); + } + + return Parent::onSelectItem(%this, %id, %text); +} + +function EditorCameraMenu::setupDefaultState(%this) +{ + // Set the Free Camera/Orbit Camera check marks + %this.checkRadioItem(0, 1, 0); + Parent::setupDefaultState(%this); +} + +function EditorFreeCameraTypeMenu::onSelectItem(%this, %id, %text) +{ + // Handle the camera type radio + %this.checkRadioItem(0, 2, %id); + + return Parent::onSelectItem(%this, %id, %text); +} + +function EditorFreeCameraTypeMenu::setupDefaultState(%this) +{ + // Set the camera type check marks + %this.checkRadioItem(0, 2, 0); + Parent::setupDefaultState(%this); +} + +function EditorCameraSpeedMenu::onSelectItem(%this, %id, %text) +{ + // Grab and set speed + %speed = getField( %this.item[%id], 2 ); + $Camera::movementSpeed = %speed; + + // Update Editor + %this.checkRadioItem(0, 6, %id); + + // Update Toolbar TextEdit + EWorldEditorCameraSpeed.setText( $Camera::movementSpeed ); + + // Update Toolbar Slider + CameraSpeedDropdownCtrlContainer-->Slider.setValue( $Camera::movementSpeed ); + + return true; +} +function EditorCameraSpeedMenu::setupDefaultState(%this) +{ + // Setup camera speed gui's. Both menu and editorgui + %this.setupGuiControls(); + + //Grab and set speed + %defaultSpeed = EditorSettings.value("LevelInformation/levels/" @ EditorGui.levelName @ "/cameraSpeed"); + if( %defaultSpeed $= "" ) + { + // Update Editor with default speed + %defaultSpeed = 25; + } + $Camera::movementSpeed = %defaultSpeed; + + // Update Toolbar TextEdit + EWorldEditorCameraSpeed.setText( %defaultSpeed ); + + // Update Toolbar Slider + CameraSpeedDropdownCtrlContainer-->Slider.setValue( %defaultSpeed ); + + Parent::setupDefaultState(%this); +} + +function EditorCameraSpeedMenu::setupGuiControls(%this) +{ + // Default levelInfo params + %minSpeed = 5; + %maxSpeed = 200; + + %speedA = EditorSettings.value("LevelInformation/levels/" @ EditorGui.levelName @ "/cameraSpeedMin"); + %speedB = EditorSettings.value("LevelInformation/levels/" @ EditorGui.levelName @ "/cameraSpeedMax"); + if( %speedA < %speedB ) + { + if( %speedA == 0 ) + { + if( %speedB > 1 ) + %minSpeed = 1; + else + %minSpeed = 0.1; + } + else + { + %minSpeed = %speedA; + } + + %maxSpeed = %speedB; + } + + // Set up the camera speed items + %inc = ( (%maxSpeed - %minSpeed) / (%this.getItemCount() - 1) ); + for( %i = 0; %i < %this.getItemCount(); %i++) + %this.item[%i] = setField( %this.item[%i], 2, (%minSpeed + (%inc * %i))); + + // Set up min/max camera slider range + eval("CameraSpeedDropdownCtrlContainer-->Slider.range = \"" @ %minSpeed @ " " @ %maxSpeed @ "\";"); +} + +////////////////////////////////////////////////////////////////////////// +// Tools Menu Handler +////////////////////////////////////////////////////////////////////////// +function EditorUtilitiesMenu::onSelectItem(%this, %id, %text) +{ + return Parent::onSelectItem(%this, %id, %text); +} + +////////////////////////////////////////////////////////////////////////// +// World Menu Handler Object Menu +////////////////////////////////////////////////////////////////////////// + +function EditorWorldMenu::onMenuSelect(%this) +{ + %selSize = EWorldEditor.getSelectionSize(); + %lockCount = EWorldEditor.getSelectionLockCount(); + %hideCount = EWorldEditor.getSelectionHiddenCount(); + + %this.enableItem(0, %lockCount < %selSize); // Lock Selection + %this.enableItem(1, %lockCount > 0); // Unlock Selection + %this.enableItem(3, %hideCount < %selSize); // Hide Selection + %this.enableItem(4, %hideCount > 0); // Show Selection + %this.enableItem(6, %selSize > 1 && %lockCount == 0); // Align bounds + %this.enableItem(7, %selSize > 1 && %lockCount == 0); // Align center + %this.enableItem(9, %selSize > 0 && %lockCount == 0); // Reset Transforms + %this.enableItem(10, %selSize > 0 && %lockCount == 0); // Reset Selected Rotation + %this.enableItem(11, %selSize > 0 && %lockCount == 0); // Reset Selected Scale + %this.enableItem(12, %selSize > 0 && %lockCount == 0); // Transform Selection + %this.enableItem(14, %selSize > 0 && %lockCount == 0); // Drop Selection + + %this.enableItem(17, %selSize > 0); // Make Prefab + %this.enableItem(18, %selSize > 0); // Explode Prefab + + %this.enableItem(20, %selSize > 1); // Mount + %this.enableItem(21, %selSize > 0); // Unmount +} + +////////////////////////////////////////////////////////////////////////// + +function EditorDropTypeMenu::onSelectItem(%this, %id, %text) +{ + // This sets up which drop script function to use when + // a drop type is selected in the menu. + EWorldEditor.dropType = getField(%this.item[%id], 2); + + %this.checkRadioItem(0, (%this.getItemCount() - 1), %id); + + return true; +} + +function EditorDropTypeMenu::setupDefaultState(%this) +{ + // Check the radio item for the currently set drop type. + + %numItems = %this.getItemCount(); + + %dropTypeIndex = 0; + for( ; %dropTypeIndex < %numItems; %dropTypeIndex ++ ) + if( getField( %this.item[ %dropTypeIndex ], 2 ) $= EWorldEditor.dropType ) + break; + + // Default to screenCenter if we didn't match anything. + if( %dropTypeIndex > (%numItems - 1) ) + %dropTypeIndex = 4; + + %this.checkRadioItem( 0, (%numItems - 1), %dropTypeIndex ); + + Parent::setupDefaultState(%this); +} + +////////////////////////////////////////////////////////////////////////// + +function EditorAlignBoundsMenu::onSelectItem(%this, %id, %text) +{ + // Have the editor align all selected objects by the selected bounds. + EWorldEditor.alignByBounds(getField(%this.item[%id], 2)); + + return true; +} + +function EditorAlignBoundsMenu::setupDefaultState(%this) +{ + // Allow the parent to set the menu's default state + Parent::setupDefaultState(%this); +} + +////////////////////////////////////////////////////////////////////////// + +function EditorAlignCenterMenu::onSelectItem(%this, %id, %text) +{ + // Have the editor align all selected objects by the selected axis. + EWorldEditor.alignByAxis(getField(%this.item[%id], 2)); + + return true; +} + +function EditorAlignCenterMenu::setupDefaultState(%this) +{ + // Allow the parent to set the menu's default state + Parent::setupDefaultState(%this); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.cs new file mode 100644 index 000000000..be068b9ed --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/menus.ed.cs @@ -0,0 +1,425 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EditorGui::buildMenus(%this) +{ + if(isObject(%this.menuBar)) + return; + + //set up %cmdctrl variable so that it matches OS standards + if( $platform $= "macos" ) + { + %cmdCtrl = "Cmd"; + %menuCmdCtrl = "Cmd"; + %quitShortcut = "Cmd Q"; + %redoShortcut = "Cmd-Shift Z"; + } + else + { + %cmdCtrl = "Ctrl"; + %menuCmdCtrl = "Alt"; + %quitShortcut = "Alt F4"; + %redoShortcut = "Ctrl Y"; + } + + // Sub menus (temporary, until MenuBuilder gets updated) + // The speed increments located here are overwritten in EditorCameraSpeedMenu::setupDefaultState. + // The new min/max for the editor camera speed range can be set in each level's levelInfo object. + if(!isObject(EditorCameraSpeedOptions)) + { + %this.cameraSpeedMenu = new PopupMenu(EditorCameraSpeedOptions) + { + superClass = "MenuBuilder"; + class = "EditorCameraSpeedMenu"; + + item[0] = "Slowest" TAB %cmdCtrl @ "-Shift 1" TAB "5"; + item[1] = "Slow" TAB %cmdCtrl @ "-Shift 2" TAB "35"; + item[2] = "Slower" TAB %cmdCtrl @ "-Shift 3" TAB "70"; + item[3] = "Normal" TAB %cmdCtrl @ "-Shift 4" TAB "100"; + item[4] = "Faster" TAB %cmdCtrl @ "-Shift 5" TAB "130"; + item[5] = "Fast" TAB %cmdCtrl @ "-Shift 6" TAB "165"; + item[6] = "Fastest" TAB %cmdCtrl @ "-Shift 7" TAB "200"; + }; + } + if(!isObject(EditorFreeCameraTypeOptions)) + { + %this.freeCameraTypeMenu = new PopupMenu(EditorFreeCameraTypeOptions) + { + superClass = "MenuBuilder"; + class = "EditorFreeCameraTypeMenu"; + + item[0] = "Standard" TAB "Ctrl 1" TAB "EditorGuiStatusBar.setCamera(\"Standard Camera\");"; + item[1] = "Orbit Camera" TAB "Ctrl 2" TAB "EditorGuiStatusBar.setCamera(\"Orbit Camera\");"; + Item[2] = "-"; + item[3] = "Smoothed" TAB "" TAB "EditorGuiStatusBar.setCamera(\"Smooth Camera\");"; + item[4] = "Smoothed Rotate" TAB "" TAB "EditorGuiStatusBar.setCamera(\"Smooth Rot Camera\");"; + }; + } + if(!isObject(EditorPlayerCameraTypeOptions)) + { + %this.playerCameraTypeMenu = new PopupMenu(EditorPlayerCameraTypeOptions) + { + superClass = "MenuBuilder"; + class = "EditorPlayerCameraTypeMenu"; + + Item[0] = "First Person" TAB "" TAB "EditorGuiStatusBar.setCamera(\"1st Person Camera\");"; + Item[1] = "Third Person" TAB "" TAB "EditorGuiStatusBar.setCamera(\"3rd Person Camera\");"; + }; + } + if(!isObject(EditorCameraBookmarks)) + { + %this.cameraBookmarksMenu = new PopupMenu(EditorCameraBookmarks) + { + superClass = "MenuBuilder"; + class = "EditorCameraBookmarksMenu"; + + //item[0] = "None"; + }; + } + %this.viewTypeMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + + item[ 0 ] = "Top" TAB "Alt 2" TAB "EditorGuiStatusBar.setCamera(\"Top View\");"; + item[ 1 ] = "Bottom" TAB "Alt 5" TAB "EditorGuiStatusBar.setCamera(\"Bottom View\");"; + item[ 2 ] = "Front" TAB "Alt 3" TAB "EditorGuiStatusBar.setCamera(\"Front View\");"; + item[ 3 ] = "Back" TAB "Alt 6" TAB "EditorGuiStatusBar.setCamera(\"Back View\");"; + item[ 4 ] = "Left" TAB "Alt 4" TAB "EditorGuiStatusBar.setCamera(\"Left View\");"; + item[ 5 ] = "Right" TAB "Alt 7" TAB "EditorGuiStatusBar.setCamera(\"Right View\");"; + item[ 6 ] = "Perspective" TAB "Alt 1" TAB "EditorGuiStatusBar.setCamera(\"Standard Camera\");"; + item[ 7 ] = "Isometric" TAB "Alt 8" TAB "EditorGuiStatusBar.setCamera(\"Isometric View\");"; + }; + + // Menu bar + %this.menuBar = new MenuBar(WorldEditorMenubar) + { + dynamicItemInsertPos = 3; + }; + + // File Menu + %fileMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorFileMenu"; + + barTitle = "File"; + }; + + + %fileMenu.appendItem("New Level" TAB "" TAB "schedule( 1, 0, \"EditorNewLevel\" );"); + %fileMenu.appendItem("Open Level..." TAB %cmdCtrl SPC "O" TAB "schedule( 1, 0, \"EditorOpenMission\" );"); + %fileMenu.appendItem("Save Level" TAB %cmdCtrl SPC "S" TAB "EditorSaveMissionMenu();"); + %fileMenu.appendItem("Save Level As..." TAB "" TAB "EditorSaveMissionAs();"); + %fileMenu.appendItem("-"); + + if( $platform $= "windows" ) + { + %fileMenu.appendItem( "Open Project in Torsion" TAB "" TAB "EditorOpenTorsionProject();" ); + %fileMenu.appendItem( "Open Level File in Torsion" TAB "" TAB "EditorOpenFileInTorsion();" ); + %fileMenu.appendItem( "-" ); + } + + %fileMenu.appendItem("Create Blank Terrain" TAB "" TAB "Canvas.pushDialog( CreateNewTerrainGui );"); + %fileMenu.appendItem("Import Terrain Heightmap" TAB "" TAB "Canvas.pushDialog( TerrainImportGui );"); + + %fileMenu.appendItem("Export Terrain Heightmap" TAB "" TAB "Canvas.pushDialog( TerrainExportGui );"); + %fileMenu.appendItem("-"); + %fileMenu.appendItem("Export To COLLADA..." TAB "" TAB "EditorExportToCollada();"); + //item[5] = "Import Terraform Data..." TAB "" TAB "Heightfield::import();"; + //item[6] = "Import Texture Data..." TAB "" TAB "Texture::import();"; + //item[7] = "-"; + //item[8] = "Export Terraform Data..." TAB "" TAB "Heightfield::saveBitmap(\"\");"; + + %fileMenu.appendItem( "-" ); + %fileMenu.appendItem( "Add FMOD Designer Audio..." TAB "" TAB "AddFMODProjectDlg.show();" ); + + %fileMenu.appendItem("-"); + %fileMenu.appendItem("Play Level" TAB "F11" TAB "Editor.close(\"PlayGui\");"); + + %fileMenu.appendItem("Exit Level" TAB "" TAB "EditorExitMission();"); + %fileMenu.appendItem("Quit" TAB %quitShortcut TAB "EditorQuitGame();"); + + %this.menuBar.insert(%fileMenu, %this.menuBar.getCount()); + + // Edit Menu + %editMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorEditMenu"; + internalName = "EditMenu"; + + barTitle = "Edit"; + + item[0] = "Undo" TAB %cmdCtrl SPC "Z" TAB "Editor.getUndoManager().undo();"; + item[1] = "Redo" TAB %redoShortcut TAB "Editor.getUndoManager().redo();"; + item[2] = "-"; + item[3] = "Cut" TAB %cmdCtrl SPC "X" TAB "EditorMenuEditCut();"; + item[4] = "Copy" TAB %cmdCtrl SPC "C" TAB "EditorMenuEditCopy();"; + item[5] = "Paste" TAB %cmdCtrl SPC "V" TAB "EditorMenuEditPaste();"; + item[6] = "Delete" TAB "Delete" TAB "EditorMenuEditDelete();"; + item[7] = "-"; + item[8] = "Deselect" TAB "X" TAB "EditorMenuEditDeselect();"; + Item[9] = "Select..." TAB "" TAB "EditorGui.toggleObjectSelectionsWindow();"; + item[10] = "-"; + item[11] = "Audio Parameters..." TAB "" TAB "EditorGui.toggleSFXParametersWindow();"; + item[12] = "Editor Settings..." TAB "" TAB "ESettingsWindow.ToggleVisibility();"; + item[13] = "Snap Options..." TAB "" TAB "ESnapOptions.ToggleVisibility();"; + item[14] = "-"; + item[15] = "Game Options..." TAB "" TAB "Canvas.pushDialog(optionsDlg);"; + item[16] = "PostEffect Manager" TAB "" TAB "Canvas.pushDialog(PostFXManager);"; + }; + %this.menuBar.insert(%editMenu, %this.menuBar.getCount()); + + // View Menu + %viewMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorViewMenu"; + internalName = "viewMenu"; + + barTitle = "View"; + + item[ 0 ] = "Visibility Layers" TAB "Alt V" TAB "VisibilityDropdownToggle();"; + item[ 1 ] = "Show Grid in Ortho Views" TAB %cmdCtrl @ "-Shift-Alt G" TAB "EditorGui.toggleOrthoGrid();"; + }; + %this.menuBar.insert(%viewMenu, %this.menuBar.getCount()); + + // Camera Menu + %cameraMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorCameraMenu"; + + barTitle = "Camera"; + + item[0] = "World Camera" TAB %this.freeCameraTypeMenu; + item[1] = "Player Camera" TAB %this.playerCameraTypeMenu; + item[2] = "-"; + Item[3] = "Toggle Camera" TAB %menuCmdCtrl SPC "C" TAB "commandToServer('ToggleCamera');"; + item[4] = "Place Camera at Selection" TAB "Ctrl Q" TAB "EWorldEditor.dropCameraToSelection();"; + item[5] = "Place Camera at Player" TAB "Alt Q" TAB "commandToServer('dropCameraAtPlayer');"; + item[6] = "Place Player at Camera" TAB "Alt W" TAB "commandToServer('DropPlayerAtCamera');"; + item[7] = "-"; + item[8] = "Fit View to Selection" TAB "F" TAB "commandToServer('EditorCameraAutoFit', EWorldEditor.getSelectionRadius()+1);"; + item[9] = "Fit View To Selection and Orbit" TAB "Alt F" TAB "EditorGuiStatusBar.setCamera(\"Orbit Camera\"); commandToServer('EditorCameraAutoFit', EWorldEditor.getSelectionRadius()+1);"; + item[10] = "-"; + item[11] = "Speed" TAB %this.cameraSpeedMenu; + item[12] = "View" TAB %this.viewTypeMenu; + item[13] = "-"; + Item[14] = "Add Bookmark..." TAB "Ctrl B" TAB "EditorGui.addCameraBookmarkByGui();"; + Item[15] = "Manage Bookmarks..." TAB "Ctrl-Shift B" TAB "EditorGui.toggleCameraBookmarkWindow();"; + item[16] = "Jump to Bookmark" TAB %this.cameraBookmarksMenu; + }; + %this.menuBar.insert(%cameraMenu, %this.menuBar.getCount()); + + // Editors Menu + %editorsMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorToolsMenu"; + + barTitle = "Editors"; + + //item[0] = "Object Editor" TAB "F1" TAB WorldEditorInspectorPlugin; + //item[1] = "Material Editor" TAB "F2" TAB MaterialEditorPlugin; + //item[2] = "-"; + //item[3] = "Terrain Editor" TAB "F3" TAB TerrainEditorPlugin; + //item[4] = "Terrain Painter" TAB "F4" TAB TerrainPainterPlugin; + //item[5] = "-"; + }; + %this.menuBar.insert(%editorsMenu, %this.menuBar.getCount()); + + // Lighting Menu + %lightingMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorLightingMenu"; + + barTitle = "Lighting"; + + item[0] = "Full Relight" TAB "Alt L" TAB "Editor.lightScene(\"\", forceAlways);"; + item[1] = "Toggle ShadowViz" TAB "" TAB "toggleShadowViz();"; + item[2] = "-"; + + // NOTE: The light managers will be inserted as the + // last menu items in EditorLightingMenu::onAdd(). + }; + %this.menuBar.insert(%lightingMenu, %this.menuBar.getCount()); + + // Tools Menu + %toolsMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorUtilitiesMenu"; + + barTitle = "Tools"; + + item[0] = "Network Graph" TAB "n" TAB "toggleNetGraph();"; + item[1] = "Profiler" TAB "ctrl F2" TAB "showMetrics(true);"; + item[2] = "Torque SimView" TAB "" TAB "tree();"; + item[3] = "Make Selected a Mesh" TAB "" TAB "makeSelectedAMesh();"; + }; + %this.menuBar.insert(%toolsMenu, %this.menuBar.getCount()); + + // Help Menu + %helpMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorHelpMenu"; + + barTitle = "Help"; + + item[0] = "Online Documentation..." TAB "Alt F1" TAB "gotoWebPage(EWorldEditor.documentationURL);"; + item[1] = "Offline User Guide..." TAB "" TAB "gotoWebPage(EWorldEditor.documentationLocal);"; + item[2] = "Offline Reference Guide..." TAB "" TAB "shellexecute(EWorldEditor.documentationReference);"; + item[3] = "Torque 3D Forums..." TAB "" TAB "gotoWebPage(EWorldEditor.forumURL);"; + }; + %this.menuBar.insert(%helpMenu, %this.menuBar.getCount()); + + // Menus that are added/removed dynamically (temporary) + + // World Menu + if(! isObject(%this.worldMenu)) + { + %this.dropTypeMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorDropTypeMenu"; + + // The onSelectItem() callback for this menu re-purposes the command field + // as the MenuBuilder version is not used. + item[0] = "at Origin" TAB "" TAB "atOrigin"; + item[1] = "at Camera" TAB "" TAB "atCamera"; + item[2] = "at Camera w/Rotation" TAB "" TAB "atCameraRot"; + item[3] = "Below Camera" TAB "" TAB "belowCamera"; + item[4] = "Screen Center" TAB "" TAB "screenCenter"; + item[5] = "at Centroid" TAB "" TAB "atCentroid"; + item[6] = "to Terrain" TAB "" TAB "toTerrain"; + item[7] = "Below Selection" TAB "" TAB "belowSelection"; + }; + + %this.alignBoundsMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorAlignBoundsMenu"; + + // The onSelectItem() callback for this menu re-purposes the command field + // as the MenuBuilder version is not used. + item[0] = "+X Axis" TAB "" TAB "0"; + item[1] = "+Y Axis" TAB "" TAB "1"; + item[2] = "+Z Axis" TAB "" TAB "2"; + item[3] = "-X Axis" TAB "" TAB "3"; + item[4] = "-Y Axis" TAB "" TAB "4"; + item[5] = "-Z Axis" TAB "" TAB "5"; + }; + + %this.alignCenterMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorAlignCenterMenu"; + + // The onSelectItem() callback for this menu re-purposes the command field + // as the MenuBuilder version is not used. + item[0] = "X Axis" TAB "" TAB "0"; + item[1] = "Y Axis" TAB "" TAB "1"; + item[2] = "Z Axis" TAB "" TAB "2"; + }; + + %this.worldMenu = new PopupMenu() + { + superClass = "MenuBuilder"; + class = "EditorWorldMenu"; + + barTitle = "Object"; + + item[0] = "Lock Selection" TAB %cmdCtrl @ " L" TAB "EWorldEditor.lockSelection(true); EWorldEditor.syncGui();"; + item[1] = "Unlock Selection" TAB %cmdCtrl @ "-Shift L" TAB "EWorldEditor.lockSelection(false); EWorldEditor.syncGui();"; + item[2] = "-"; + item[3] = "Hide Selection" TAB %cmdCtrl @ " H" TAB "EWorldEditor.hideSelection(true); EWorldEditor.syncGui();"; + item[4] = "Show Selection" TAB %cmdCtrl @ "-Shift H" TAB "EWorldEditor.hideSelection(false); EWorldEditor.syncGui();"; + item[5] = "-"; + item[6] = "Align Bounds" TAB %this.alignBoundsMenu; + item[7] = "Align Center" TAB %this.alignCenterMenu; + item[8] = "-"; + item[9] = "Reset Transforms" TAB "Ctrl R" TAB "EWorldEditor.resetTransforms();"; + item[10] = "Reset Selected Rotation" TAB "" TAB "EWorldEditor.resetSelectedRotation();"; + item[11] = "Reset Selected Scale" TAB "" TAB "EWorldEditor.resetSelectedScale();"; + item[12] = "Transform Selection..." TAB "Ctrl T" TAB "ETransformSelection.ToggleVisibility();"; + item[13] = "-"; + //item[13] = "Drop Camera to Selection" TAB "Ctrl Q" TAB "EWorldEditor.dropCameraToSelection();"; + //item[14] = "Add Selection to Instant Group" TAB "" TAB "EWorldEditor.addSelectionToAddGroup();"; + item[14] = "Drop Selection" TAB "Ctrl D" TAB "EWorldEditor.dropSelection();"; + //item[15] = "-"; + item[15] = "Drop Location" TAB %this.dropTypeMenu; + Item[16] = "-"; + Item[17] = "Make Selection Prefab" TAB "" TAB "EditorMakePrefab();"; + Item[18] = "Explode Selected Prefab" TAB "" TAB "EditorExplodePrefab();"; + Item[19] = "-"; + Item[20] = "Mount Selection A to B" TAB "" TAB "EditorMount();"; + Item[21] = "Unmount Selected Object" TAB "" TAB "EditorUnmount();"; + }; + } +} + +////////////////////////////////////////////////////////////////////////// + +function EditorGui::attachMenus(%this) +{ + %this.menuBar.attachToCanvas(Canvas, 0); +} + +function EditorGui::detachMenus(%this) +{ + %this.menuBar.removeFromCanvas(); +} + +function EditorGui::setMenuDefaultState(%this) +{ + if(! isObject(%this.menuBar)) + return 0; + + for(%i = 0;%i < %this.menuBar.getCount();%i++) + { + %menu = %this.menuBar.getObject(%i); + %menu.setupDefaultState(); + } + + %this.worldMenu.setupDefaultState(); +} + +////////////////////////////////////////////////////////////////////////// + +function EditorGui::findMenu(%this, %name) +{ + if(! isObject(%this.menuBar)) + return 0; + + for(%i = 0;%i < %this.menuBar.getCount();%i++) + { + %menu = %this.menuBar.getObject(%i); + + if(%name $= %menu.barTitle) + return %menu; + } + + return 0; +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/objectSnapOptions.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/objectSnapOptions.ed.cs new file mode 100644 index 000000000..33c231e53 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/objectSnapOptions.ed.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + + +function ESnapOptions::onWake( %this ) +{ + ESnapOptionsTabTerrain-->NoAlignment.setStateOn(1); + + ESnapOptionsTabSoft-->NoAlignment.setStateOn(1); + ESnapOptionsTabSoft-->RenderSnapBounds.setStateOn(1); + ESnapOptionsTabSoft-->SnapBackfaceTolerance.setText(EWorldEditor.getSoftSnapBackfaceTolerance()); +} + +function ESnapOptions::hideDialog( %this ) +{ + %this.setVisible(false); + SnapToBar-->snappingSettingsBtn.setStateOn(false); +} + +function ESnapOptions::ToggleVisibility() +{ + if ( ESnapOptions.visible ) + { + ESnapOptions.setVisible(false); + SnapToBar-->snappingSettingsBtn.setStateOn(false); + } + else + { + ESnapOptions.setVisible(true); + ESnapOptions.selectWindow(); + ESnapOptions.setCollapseGroup(false); + SnapToBar-->snappingSettingsBtn.setStateOn(true); + } +} + +function ESnapOptions::setTerrainSnapAlignment( %this, %val ) +{ + EWorldEditor.setTerrainSnapAlignment(%val); +} + +function ESnapOptions::setSoftSnapAlignment( %this, %val ) +{ + EWorldEditor.setSoftSnapAlignment(%val); +} + +function ESnapOptions::setSoftSnapSize( %this ) +{ + %val = ESnapOptions-->SnapSize.getText(); + + EWorldEditor.setSoftSnapSize(%val); + EWorldEditor.syncGui(); +} + +function ESnapOptions::setGridSnapSize( %this ) +{ + %val = ESnapOptions-->GridSize.getText(); + + EWorldEditor.setGridSize( %val ); +} + +function ESnapOptions::toggleRenderSnapBounds( %this ) +{ + EWorldEditor.softSnapRender( ESnapOptionsTabSoft-->RenderSnapBounds.getValue() ); +} + +function ESnapOptions::toggleRenderSnappedTriangle( %this ) +{ + EWorldEditor.softSnapRenderTriangle( ESnapOptionsTabSoft-->RenderSnappedTriangle.getValue() ); +} + +function ESnapOptions::getSoftSnapBackfaceTolerance( %this ) +{ + %val = ESnapOptions-->SnapBackfaceTolerance.getText(); + + EWorldEditor.setSoftSnapBackfaceTolerance(%val); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/transformSelection.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/transformSelection.ed.cs new file mode 100644 index 000000000..7777e4acb --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/transformSelection.ed.cs @@ -0,0 +1,448 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function ETransformSelection::onWake( %this ) +{ + // Make everything relative + ETransformSelection-->PosRelative.setStateOn( true ); + ETransformSelection-->RotRelative.setStateOn( true ); + ETransformSelection-->ScaleRelative.setStateOn( true ); + ETransformSelection-->SizeRelative.setStateOn( false ); + + ETransformSelection-->GetPosButton.setActive( false ); + ETransformSelection-->GetRotButton.setActive( false ); + ETransformSelection-->GetScaleButton.setActive( false ); + ETransformSelection-->GetSizeButton.setActive( false ); + + // Size is always local + ETransformSelection-->SizeLocal.setStateOn( true ); + ETransformSelection-->SizeLocal.setActive( false ); + + ETransformSelection-->ScaleTabBook.selectPage( 0 ); // Scale page + + ETransformSelection-->ApplyButton.setActive( false ); + + EWorldEditor.ETransformSelectionDisplayed = false; +} + +function ETransformSelection::onVisible( %this, %state ) +{ + // If we are made visible, sync to the world editor + // selection. + + if( %state ) + %this.onSelectionChanged(); +} + +function ETransformSelection::hideDialog( %this ) +{ + %this.setVisible(false); + EWorldEditor.ETransformSelectionDisplayed = false; +} + +function ETransformSelection::ToggleVisibility( %this ) +{ + if ( ETransformSelection.visible ) + { + ETransformSelection.setVisible(false); + EWorldEditor.ETransformSelectionDisplayed = false; + } + else + { + ETransformSelection.setVisible(true); + ETransformSelection.selectWindow(); + ETransformSelection.setCollapseGroup(false); + EWorldEditor.ETransformSelectionDisplayed = true; + } +} + +function ETransformSelection::disableAllButtons( %this ) +{ + ETransformSelection-->GetPosButton.setActive( false ); + ETransformSelection-->GetRotButton.setActive( false ); + ETransformSelection-->GetScaleButton.setActive( false ); + ETransformSelection-->GetSizeButton.setActive( false ); + + ETransformSelection-->ApplyButton.setActive( false ); +} + +function ETransformSelection::onSelectionChanged( %this ) +{ + // Count the number of selected SceneObjects. There are + // other object classes that could be selected, such + // as SimGroups. + %count = EWorldEditor.getSelectionSize(); + %sceneObjects = 0; + %globalBoundsObjects = 0; + for( %i=0; %i<%count; %i++) + { + %obj = EWorldEditor.getSelectedObject( %i ); + if( %obj.isMemberOfClass("SceneObject") ) + { + %sceneObjects++; + + if( %obj.isGlobalBounds() ) + { + %globalBoundsObjects++; + } + } + } + + if( %sceneObjects == 0 ) + { + // With nothing selected, disable all Get buttons + ETransformSelection.disableAllButtons(); + } + else if( %sceneObjects == 1 ) + { + // With one selected, all Get buttons are active + ETransformSelection-->GetPosButton.setActive( true ); + ETransformSelection-->GetRotButton.setActive( true ); + + // Special case for Scale and Size for global bounds objects + if( %globalBoundsObjects == 1 ) + { + ETransformSelection-->GetSizeButton.setActive( false ); + ETransformSelection-->GetScaleButton.setActive( false ); + } + else + { + ETransformSelection-->GetSizeButton.setActive( true ); + ETransformSelection-->GetScaleButton.setActive( true ); + } + + ETransformSelection-->ApplyButton.setActive( true ); + } + else + { + // With more than one selected, only the position button + // is active + ETransformSelection-->GetPosButton.setActive( true ); + ETransformSelection-->GetRotButton.setActive( false ); + ETransformSelection-->GetScaleButton.setActive( false ); + ETransformSelection-->GetSizeButton.setActive( false ); + + ETransformSelection-->ApplyButton.setActive( true ); + + // If both RotRelative and RotLocal are unchecked, then go with RotLocal + if( ETransformSelection-->RotRelative.getValue() == 0 && ETransformSelection-->RotLocal.getValue() == 0 ) + { + ETransformSelection-->RotLocal.setStateOn( true ); + } + } +} + +function ETransformSelection::apply( %this ) +{ + %position = ETransformSelection-->DoPosition.getValue(); + %p = ETransformSelection-->PosX.getValue() SPC ETransformSelection-->PosY.getValue() SPC ETransformSelection-->PosZ.getValue(); + %relativePos = ETransformSelection-->PosRelative.getValue(); + + %rotate = ETransformSelection-->DoRotation.getValue(); + %r = mDegToRad(ETransformSelection-->Pitch.getValue()) SPC mDegToRad(ETransformSelection-->Bank.getValue()) SPC mDegToRad(ETransformSelection-->Heading.getValue()); + %relativeRot = ETransformSelection-->RotRelative.getValue(); + %rotLocal = ETransformSelection-->RotLocal.getValue(); + + // We need to check which Tab page is active + if( ETransformSelection-->ScaleTabBook.getSelectedPage() == 0 ) + { + // Scale Page + %scale = ETransformSelection-->DoScale.getValue(); + %s = ETransformSelection-->ScaleX.getValue() SPC ETransformSelection-->ScaleY.getValue() SPC ETransformSelection-->ScaleZ.getValue(); + %sRelative = ETransformSelection-->ScaleRelative.getValue(); + %sLocal = ETransformSelection-->ScaleLocal.getValue(); + + %size = false; + } + else + { + // Size Page + %size = ETransformSelection-->DoSize.getValue(); + %s = ETransformSelection-->SizeX.getValue() SPC ETransformSelection-->SizeY.getValue() SPC ETransformSelection-->SizeZ.getValue(); + %sRelative = ETransformSelection-->SizeRelative.getValue(); + %sLocal = ETransformSelection-->SizeLocal.getValue(); + + %scale = false; + } + + EWorldEditor.transformSelection(%position, %p, %relativePos, %rotate, %r, %relativeRot, %rotLocal, %scale ? 1 : (%size ? 2 : 0), %s, %sRelative, %sLocal); +} + +function ETransformSelection::getAbsPosition( %this ) +{ + %pos = EWorldEditor.getSelectionCentroid(); + ETransformSelection-->PosX.setText(getWord(%pos, 0)); + ETransformSelection-->PosY.setText(getWord(%pos, 1)); + ETransformSelection-->PosZ.setText(getWord(%pos, 2)); + + // Turn off relative as we're populating absolute values + ETransformSelection-->PosRelative.setValue(0); + + // Finally, set the Position check box as active. The user + // likely wants this if they're getting the position. + ETransformSelection-->DoPosition.setValue(1); +} + +function ETransformSelection::getAbsRotation( %this ) +{ + %count = EWorldEditor.getSelectionSize(); + + // If we have more than one SceneObject selected, + // we must exit. + %obj = -1; + for( %i=0; %i<%count; %i++) + { + %test = EWorldEditor.getSelectedObject( %i ); + if( %test.isMemberOfClass("SceneObject") ) + { + if( %obj != -1 ) + return; + + %obj = %test; + } + } + + if( %obj == -1 ) + { + // No SceneObjects selected + return; + } + + %rot = %obj.getEulerRotation(); + ETransformSelection-->Pitch.setText(getWord(%rot, 0)); + ETransformSelection-->Bank.setText(getWord(%rot, 1)); + ETransformSelection-->Heading.setText(getWord(%rot, 2)); + + // Turn off relative as we're populating absolute values. + // Of course this means that we need to set local on. + ETransformSelection-->RotRelative.setValue(0); + ETransformSelection-->RotLocal.setValue(1); + + // Finally, set the Rotation check box as active. The user + // likely wants this if they're getting the position. + ETransformSelection-->DoRotation.setValue(1); +} + +function ETransformSelection::getAbsScale( %this ) +{ + %count = EWorldEditor.getSelectionSize(); + + // If we have more than one SceneObject selected, + // we must exit. + %obj = -1; + for( %i=0; %i<%count; %i++) + { + %test = EWorldEditor.getSelectedObject( %i ); + if( %test.isMemberOfClass("SceneObject") ) + { + if( %obj != -1 ) + return; + + %obj = %test; + } + } + + if( %obj == -1 ) + { + // No SceneObjects selected + return; + } + + %scale = %obj.scale; + %scalex = getWord(%scale, 0); + ETransformSelection-->ScaleX.setText(%scalex); + if( ETransformSelectionScaleProportional.getValue() == false ) + { + ETransformSelection-->ScaleY.setText(getWord(%scale, 1)); + ETransformSelection-->ScaleZ.setText(getWord(%scale, 2)); + } + else + { + ETransformSelection-->ScaleY.setText(%scalex); + ETransformSelection-->ScaleZ.setText(%scalex); + } + + // Turn off relative as we're populating absolute values + ETransformSelection-->ScaleRelative.setValue(0); + + // Finally, set the Scale check box as active. The user + // likely wants this if they're getting the position. + ETransformSelection-->DoScale.setValue(1); +} + +function ETransformSelection::getAbsSize( %this ) +{ + %count = EWorldEditor.getSelectionSize(); + + // If we have more than one SceneObject selected, + // we must exit. + %obj = -1; + for( %i=0; %i<%count; %i++) + { + %test = EWorldEditor.getSelectedObject( %i ); + if( %test.isMemberOfClass("SceneObject") ) + { + if( %obj != -1 ) + return; + + %obj = %test; + } + } + + if( %obj == -1 ) + { + // No SceneObjects selected + return; + } + + %size = %obj.getObjectBox(); + %scale = %obj.getScale(); + + %sizex = (getWord(%size, 3) - getWord(%size, 0)) * getWord(%scale, 0); + ETransformSelection-->SizeX.setText( %sizex ); + if( ETransformSelectionSizeProportional.getValue() == false ) + { + ETransformSelection-->SizeY.setText( (getWord(%size, 4) - getWord(%size, 1)) * getWord(%scale, 1) ); + ETransformSelection-->SizeZ.setText( (getWord(%size, 5) - getWord(%size, 2)) * getWord(%scale, 2) ); + } + else + { + ETransformSelection-->SizeY.setText( %sizex ); + ETransformSelection-->SizeZ.setText( %sizex ); + } + + // Turn off relative as we're populating absolute values + ETransformSelection-->SizeRelative.setValue(0); + + // Finally, set the Size check box as active. The user + // likely wants this if they're getting the position. + ETransformSelection-->DoSize.setValue(1); +} + +function ETransformSelection::RotRelativeChanged( %this ) +{ + if( ETransformSelection-->RotRelative.getValue() == 0 ) + { + // With absolute rotation, it must happen locally + ETransformSelection-->RotLocal.setStateOn( true ); + } +} + +function ETransformSelection::RotLocalChanged( %this ) +{ + if( ETransformSelection-->RotLocal.getValue() == 0 ) + { + // Non-local rotation can only happen relatively + ETransformSelection-->RotRelative.setStateOn( true ); + } +} + +//----------------------------------------------------------------------------- + +function ETransformSelectionScaleProportional::onClick( %this ) +{ + if( %this.getValue() == 1 ) + { + %scalex = ETransformSelection-->ScaleX.getValue(); + ETransformSelection-->ScaleY.setValue( %scalex ); + ETransformSelection-->ScaleZ.setValue( %scalex ); + + ETransformSelection-->ScaleY.setActive( false ); + ETransformSelection-->ScaleZ.setActive( false ); + } + else + { + ETransformSelection-->ScaleY.setActive( true ); + ETransformSelection-->ScaleZ.setActive( true ); + } + + Parent::onClick(%this); +} + +function ETransformSelectionSizeProportional::onClick( %this ) +{ + if( %this.getValue() == 1 ) + { + %scalex = ETransformSelection-->SizeX.getValue(); + ETransformSelection-->SizeY.setValue( %scalex ); + ETransformSelection-->SizeZ.setValue( %scalex ); + + ETransformSelection-->SizeY.setActive( false ); + ETransformSelection-->SizeZ.setActive( false ); + } + else + { + ETransformSelection-->SizeY.setActive( true ); + ETransformSelection-->SizeZ.setActive( true ); + } + + Parent::onClick(%this); +} + +//----------------------------------------------------------------------------- + +function ETransformSelectionButtonClass::onClick( %this ) +{ + %id = %this.getRoot().getFirstResponder(); + if( %id > -1 && ETransformSelection.controlIsChild(%id) ) + { + (%id).clearFirstResponder(true); + } +} + +function ETransformSelectionCheckBoxClass::onClick( %this ) +{ + %id = %this.getRoot().getFirstResponder(); + if( %id > -1 && ETransformSelection.controlIsChild(%id) ) + { + (%id).clearFirstResponder(true); + } +} + +//----------------------------------------------------------------------------- + +function ETransformSelectionTextEdit::onGainFirstResponder( %this ) +{ + if( %this.isActive() ) + { + %this.selectAllText(); + } +} + +function ETransformSelectionTextEdit::onValidate( %this ) +{ + if( %this.getInternalName() $= "ScaleX" && ETransformSelectionScaleProportional.getValue() == true ) + { + // Set the Y and Z values to match + %scalex = ETransformSelection-->ScaleX.getValue(); + ETransformSelection-->ScaleY.setValue( %scalex ); + ETransformSelection-->ScaleZ.setValue( %scalex ); + } + + if( %this.getInternalName() $= "SizeX" && ETransformSelectionSizeProportional.getValue() == true ) + { + // Set the Y and Z values to match + %sizex = ETransformSelection-->SizeX.getValue(); + ETransformSelection-->SizeY.setValue( %sizex ); + ETransformSelection-->SizeZ.setValue( %sizex ); + } +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/undoManager.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/undoManager.ed.cs new file mode 100644 index 000000000..7f05ffd19 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/undoManager.ed.cs @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EUndoManager::onUndo( %this ) +{ +} + +function EUndoManager::onRedo( %this ) +{ +} + +function EUndoManager::onAddUndo( %this ) +{ +} + +function EUndoManager::onRemoveUndo( %this ) +{ +} + +function EUndoManager::onClear( %this ) +{ +} + +function EUndoManager::updateUndoMenu( %this, %editMenu ) +{ + // TODO: If we ever fix the TerrainEditor and WorldEditor + // to have descriptive UndoAction names then we can change + // the text as part of the menu update. + + %undoName = %this.getNextUndoName(); + %redoName = %this.getNextRedoName(); + + %editMenu.setItemName( 0, "Undo " @ %undoName ); + %editMenu.setItemName( 1, "Redo " @ %redoName ); + + %editMenu.enableItem( 0, %undoName !$= "" ); + %editMenu.enableItem( 1, %redoName !$= "" ); +} + + +/// A helper for submitting a creation undo action. +function MECreateUndoAction::submit( %undoObject ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + // Create the undo action. + %action = new MECreateUndoAction() + { + actionName = "Create " @ %undoObject.getClassName(); + }; + + // Restore the instant group. + popInstantGroup(); + + // Set the object to undo. + %action.addObject( %undoObject ); + + // Submit it. + %action.addToManager( Editor.getUndoManager() ); +} + +function MECreateUndoAction::onUndone( %this ) +{ + EWorldEditor.syncGui(); +} + +function MECreateUndoAction::onRedone( %this ) +{ + EWorldEditor.syncGui(); +} + + +/// A helper for submitting a delete undo action. +/// If %wordSeperated is not specified or is false it is assumed %deleteObjects +/// is tab sperated. +function MEDeleteUndoAction::submit( %deleteObjects, %wordSeperated ) +{ + // The instant group will try to add our + // UndoAction if we don't disable it. + pushInstantGroup(); + + // Create the undo action. + %action = new MEDeleteUndoAction() + { + actionName = "Delete"; + }; + + // Restore the instant group. + popInstantGroup(); + + // Add the deletion objects to the action which + // will take care of properly deleting them. + %deleteObjects = trim( %deleteObjects ); + + if ( %wordSeperated ) + { + %count = getWordCount( %deleteObjects ); + for ( %i = 0; %i < %count; %i++ ) + { + %object = getWord( %deleteObjects, %i ); + %action.deleteObject( %object ); + } + } + else + { + %count = getFieldCount( %deleteObjects ); + for ( %i = 0; %i < %count; %i++ ) + { + %object = getField( %deleteObjects, %i ); + %action.deleteObject( %object ); + } + } + + // Submit it. + %action.addToManager( Editor.getUndoManager() ); +} + +function MEDeleteUndoAction::onUndone( %this ) +{ + EWorldEditor.syncGui(); +} + +function MEDeleteUndoAction::onRedone( %this ) +{ + EWorldEditor.syncGui(); +} diff --git a/Templates/BaseGame/game/tools/worldEditor/scripts/visibilityLayer.ed.cs b/Templates/BaseGame/game/tools/worldEditor/scripts/visibilityLayer.ed.cs new file mode 100644 index 000000000..268e4b0b3 --- /dev/null +++ b/Templates/BaseGame/game/tools/worldEditor/scripts/visibilityLayer.ed.cs @@ -0,0 +1,201 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +function EVisibility::onWake( %this ) +{ + // Create the array if it + // doesn't already exist. + if ( !isObject( %this.array ) ) + %this.array = new ArrayObject(); + + // Create the array if it + // doesn't already exist. + if ( !isObject( %this.classArray ) ) + { + %this.classArray = new ArrayObject(); + %this.addClassOptions(); + } + + %this.updateOptions(); + +} + +function EVisibility::updateOptions( %this ) +{ + // First clear the stack control. + %this-->theVisOptionsList.clear(); + + // Go through all the + // parameters in our array and + // create a check box for each. + for ( %i = 0; %i < %this.array.count(); %i++ ) + { + %text = " " @ %this.array.getValue( %i ); + %val = %this.array.getKey( %i ); + %var = getWord( %val, 0 ); + %toggleFunction = getWord( %val, 1 ); + + %textLength = strlen( %text ); + + %cmd = ""; + if ( %toggleFunction !$= "" ) + %cmd = %toggleFunction @ "( $thisControl.getValue() );"; + + %checkBox = new GuiCheckBoxCtrl() + { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxListProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = (%textLength * 4) @ " 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = %var; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + text = %text; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + Command = %cmd; + }; + + %this-->theVisOptionsList.addGuiControl( %checkBox ); + } +} + +function EVisibility::addOption( %this, %text, %varName, %toggleFunction ) +{ + // Create the array if it + // doesn't already exist. + if ( !isObject( %this.array ) ) + %this.array = new ArrayObject(); + + %this.array.push_back( %varName @ " " @ %toggleFunction, %text ); + %this.array.uniqueKey(); + %this.array.sortd(); + %this.updateOptions(); +} + +function EVisibility::addClassOptions( %this ) +{ + %visList = %this-->theClassVisList; + %selList = %this-->theClassSelList; + + // First clear the stack control. + + %visList.clear(); + %selList.clear(); + + %classList = enumerateConsoleClasses( "SceneObject" ); + %classCount = getFieldCount( %classList ); + + for ( %i = 0; %i < %classCount; %i++ ) + { + %className = getField( %classList, %i ); + %this.classArray.push_back( %className ); + } + + // Remove duplicates and sort by key. + %this.classArray.uniqueKey(); + %this.classArray.sortkd(); + + // Go through all the + // parameters in our array and + // create a check box for each. + for ( %i = 0; %i < %this.classArray.count(); %i++ ) + { + %class = %this.classArray.getKey( %i ); + + %visVar = "$" @ %class @ "::isRenderable"; + %selVar = "$" @ %class @ "::isSelectable"; + + %textLength = strlen( %class ); + %text = " " @ %class; + + // Add visibility toggle. + + %visCheckBox = new GuiCheckBoxCtrl() + { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxListFlipedProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = (%textLength * 4) @ " 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = %visVar; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Show/hide all " @ %class @ " objects."; + text = %text; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + + %visList.addGuiControl( %visCheckBox ); + + // Add selectability toggle. + + %selCheckBox = new GuiCheckBoxCtrl() + { + canSaveDynamicFields = "0"; + isContainer = "0"; + Profile = "ToolsGuiCheckBoxListFlipedProfile"; + HorizSizing = "right"; + VertSizing = "bottom"; + Position = "0 0"; + Extent = (%textLength * 4) @ " 18"; + MinExtent = "8 2"; + canSave = "1"; + Visible = "1"; + Variable = %selVar; + tooltipprofile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + tooltip = "Enable/disable selection of all " @ %class @ " objects."; + text = %text; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + useInactiveState = "0"; + }; + + %selList.addGuiControl( %selCheckBox ); + } +} + +function togglePhysicsDebugViz( %enable ) +{ + if(physicsPluginPresent()) + { + physicsDebugDraw(%enable); + } +} diff --git a/Templates/BaseGame/source/readme.txt b/Templates/BaseGame/source/readme.txt new file mode 100644 index 000000000..e79addd05 --- /dev/null +++ b/Templates/BaseGame/source/readme.txt @@ -0,0 +1,7 @@ +Your project specific source files and subfolders go into this directory. + +Simply add them to this folder and then regenerate your project +Visual Studio Solution/XCode workspace with the Torque Toolbox. + + + diff --git a/Templates/BaseGame/source/torqueConfig.h b/Templates/BaseGame/source/torqueConfig.h new file mode 100644 index 000000000..365476b19 --- /dev/null +++ b/Templates/BaseGame/source/torqueConfig.h @@ -0,0 +1,246 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// 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. +//----------------------------------------------------------------------------- + +#ifndef _TORQUECONFIG_H_ +#define _TORQUECONFIG_H_ + +//----------------------------------------------------------------------------- +//Hi, and welcome to the Torque Config file. +// +//This file is a central reference for the various configuration flags that +//you'll be using when controlling what sort of a Torque build you have. In +//general, the information here is global for your entire codebase, applying +//not only to your game proper, but also to all of your tools. + +/// What's the name of your application? Used in a variety of places. +#define TORQUE_APP_NAME "Full" + +#define TORQUE_NET_DEFAULT_MULTICAST_ADDRESS "ff04::7467:656E:6574:776B" + +/// What version of the application specific source code is this? +/// +/// Version number is major * 1000 + minor * 100 + revision * 10. +#define TORQUE_APP_VERSION 1000 + +/// Human readable application version string. +#define TORQUE_APP_VERSION_STRING "1.0" + +/// Define me if you want to enable multithreading support. +#ifndef TORQUE_MULTITHREAD +#define TORQUE_MULTITHREAD +#endif + +/// Define me if you want to disable Torque memory manager. +#ifndef TORQUE_DISABLE_MEMORY_MANAGER +#define TORQUE_DISABLE_MEMORY_MANAGER +#endif + +/// The improved SimDictionary uses C++11 and is designed for games where +/// there are over 10000 simobjects active normally. To enable the new +/// SimDictionary just uncomment the line below. +//#define USE_NEW_SIMDICTIONARY + +/// Define me if you want to disable the virtual mount system. +//#define TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM + +/// Define me if you want to disable looking for the root of a given path +/// within a zip file. This means that the zip file name itself must be +/// the root of the path. Requires the virtual mount system to be active. +//#define TORQUE_DISABLE_FIND_ROOT_WITHIN_ZIP + +//Uncomment this define if you want to use the alternative zip support where you can +//define your directories and files inside the zip just like you would on disk +//instead of the default zip support that treats the zip as an extra directory. +//#define TORQUE_ZIP_DISK_LAYOUT + +/// Define me if you don't want Torque to compile dso's +#define TORQUE_NO_DSO_GENERATION + +// Define me if this build is a tools build + +#ifndef TORQUE_PLAYER +# define TORQUE_TOOLS +#else +# undef TORQUE_TOOLS +#endif + +/// Define me if you want to enable the profiler. +/// See also the TORQUE_SHIPPING block below +//#define TORQUE_ENABLE_PROFILER + +/// Define me to enable debug mode; enables a great number of additional +/// sanity checks, as well as making AssertFatal and AssertWarn do something. +/// This is usually defined by the build target. +//#define TORQUE_DEBUG + +/// Define me if this is a shipping build; if defined I will instruct Torque +/// to batten down some hatches and generally be more "final game" oriented. +/// Notably this disables a liberal resource manager file searching, and +/// console help strings. +//#define TORQUE_SHIPPING + +/// Define me to enable a variety of network debugging aids. +/// +/// - NetConnection packet logging. +/// - DebugChecksum guards to detect mismatched pack/unpacks. +/// - Detection of invalid destination ghosts. +/// +//#define TORQUE_DEBUG_NET + +/// Define me to enable detailed console logging of net moves. +//#define TORQUE_DEBUG_NET_MOVES + +/// Enable this define to change the default Net::MaxPacketDataSize +/// Do this at your own risk since it has the potential to cause packets +/// to be split up by old routers and Torque does not have a mechanism to +/// stitch split packets back together. Using this define can be very useful +/// in controlled network hardware environments (like a LAN) or for singleplayer +/// games (like BArricade and its large paths) +//#define MAXPACKETSIZE 1500 + +/// Modify me to enable metric gathering code in the renderers. +/// +/// 0 does nothing; higher numbers enable higher levels of metric gathering. +//#define TORQUE_GATHER_METRICS 0 + +/// Define me if you want to enable debug guards in the memory manager. +/// +/// Debug guards are known values placed before and after every block of +/// allocated memory. They are checked periodically by Memory::validate(), +/// and if they are modified (indicating an access to memory the app doesn't +/// "own"), an error is flagged (ie, you'll see a crash in the memory +/// manager's validate code). Using this and a debugger, you can track down +/// memory corruption issues quickly. +//#define TORQUE_DEBUG_GUARD + +/// Define me if you want to enable instanced-static behavior +//#define TORQUE_ENABLE_THREAD_STATICS + +/// Define me if you want to gather static-usage metrics +//#define TORQUE_ENABLE_THREAD_STATIC_METRICS + +/// Define me if you want to enable debug guards on the FrameAllocator. +/// +/// This is similar to the above memory manager guards, but applies only to the +/// fast FrameAllocator temporary pool memory allocations. The guards are only +/// checked when the FrameAllocator frees memory (when it's water mark changes). +/// This is most useful for detecting buffer overruns when using FrameTemp<> . +/// A buffer overrun in the FrameAllocator is unlikely to cause a crash, but may +/// still result in unexpected behavior, if other FrameTemp's are stomped. +//#define FRAMEALLOCATOR_DEBUG_GUARD + +/// This #define is used by the FrameAllocator to align starting addresses to +/// be byte aligned to this value. This is important on the 360 and possibly +/// on other platforms as well. Use this #define anywhere alignment is needed. +/// +/// NOTE: Do not change this value per-platform unless you have a very good +/// reason for doing so. It has the potential to cause inconsistencies in +/// memory which is allocated and expected to be contiguous. +/// +///@ TODO: Make sure that everywhere this should be used, it is being used. +#define TORQUE_BYTE_ALIGNMENT 4 + +/// This #define should be set if the engine should use a 32-bit format for +/// 24-bit textures. The most notable case is converting RGB->RGBX for various +/// reasons. + +// CodeReview: It may be worth determining this at run-time. Right now I just +// want to wrap code which forces the conversion from 24->32 in a common +// #define so it is easily turned on/off for the problems we are encountering in +// the lighting. [5/11/2007 Pat] +//#define TORQUE_FORCE_24_BIT_TO_32_BIT_TEXTURES + +/// This #define is used by the FrameAllocator to set the size of the frame. +/// +/// It was previously set to 3MB but I've increased it to 32MB due to the +/// FrameAllocator being used as temporary storage for bitmaps in the D3D9 +/// texture manager. +#define TORQUE_FRAME_SIZE 32 << 20 + +// Finally, we define some dependent #defines. This enables some subsidiary +// functionality to get automatically turned on in certain configurations. + +#ifdef TORQUE_DEBUG + + #define TORQUE_GATHER_METRICS 0 + #define TORQUE_ENABLE_PROFILE_PATH + #ifndef TORQUE_DEBUG_GUARD + #define TORQUE_DEBUG_GUARD + #endif + #ifndef TORQUE_NET_STATS + #define TORQUE_NET_STATS + #endif + + // Enables the C++ assert macros AssertFatal, AssertWarn, etc. + #define TORQUE_ENABLE_ASSERTS + +#endif + +#ifdef TORQUE_RELEASE + // If it's not DEBUG, it's a RELEASE build, put appropriate things here. +#endif + +#ifdef TORQUE_SHIPPING + + // TORQUE_SHIPPING flags here. + +#else + + // Enable the profiler by default, if we're not doing a shipping build. + #define TORQUE_ENABLE_PROFILER + + // Enable the TorqueScript assert() instruction if not shipping. + #define TORQUE_ENABLE_SCRIPTASSERTS + + // We also enable GFX debug events for use in Pix and other graphics + // debugging tools. + #define TORQUE_ENABLE_GFXDEBUGEVENTS + +#endif + +#ifdef TORQUE_TOOLS +# define TORQUE_INSTANCE_EXCLUSION "TorqueToolsTest" +#else +# define TORQUE_INSTANCE_EXCLUSION "TorqueTest" +#endif + +// This is the lighting system thats enabled by default when the scene graphs are created. +//#define DEFAULT_LIGHTING_SYSTEM "SynapseGaming Lighting Kit" +#define DEFAULT_LIGHTING_SYSTEM "Basic Lighting" + +// Someday, it might make sense to do some pragma magic here so we error +// on inconsistent flags. + +// The Xbox360 has it's own profiling tools, the Torque Profiler should not be used +#ifdef TORQUE_OS_XENON +# ifdef TORQUE_ENABLE_PROFILER +# undef TORQUE_ENABLE_PROFILER +# endif +# +# ifdef TORQUE_ENABLE_PROFILE_PATH +# undef TORQUE_ENABLE_PROFILE_PATH +#endif +#endif + + +#endif // _TORQUECONFIG_H_ + diff --git a/Templates/BaseGame/thumb.png b/Templates/BaseGame/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..abb73e9fc832e3923f785a7d48daef4d7829ceb2 GIT binary patch literal 51416 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 N002ovPDHLkV1iiAN;d!i literal 0 HcmV?d00001 diff --git a/Templates/Empty/game/core/main.cs b/Templates/Empty/game/core/main.cs index f666c948c..9888fa4de 100644 --- a/Templates/Empty/game/core/main.cs +++ b/Templates/Empty/game/core/main.cs @@ -28,6 +28,12 @@ $WORD::BITDEPTH = 3; $WORD::REFRESH = 4; $WORD::AA = 5; +//We need to hook the missing/warn material stuff early, so do it here +$Core::MissingTexturePath = "core/art/missingTexture"; +$Core::UnAvailableTexturePath = "core/art/unavailable"; +$Core::WarningTexturePath = "core/art/warnMat"; +$Core::CommonShaderPath = "shaders/common"; + //--------------------------------------------------------------------------------------------- // CorePackage // Adds functionality for this mod to some standard functions. diff --git a/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs b/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs index b88f31305..a61a3af9d 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs @@ -55,6 +55,7 @@ singleton PostEffect( GammaPostFX ) texture[0] = "$backBuffer"; texture[1] = $HDRPostFX::colorCorrectionRamp; + targetFormat = getBestHDRFormat(); }; function GammaPostFX::preProcess( %this ) diff --git a/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs b/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs index bef075ec4..e09f45a3c 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs @@ -46,7 +46,7 @@ singleton ShaderData( MLAA_EdgeDetectionShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/offsetV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/edgeDetectionP.hlsl"; - + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/offsetV.glsl"; OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl"; @@ -75,7 +75,7 @@ singleton ShaderData( MLAA_BlendWeightCalculationShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/passthruV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl"; - + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/passthruV.glsl"; OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl"; @@ -162,7 +162,7 @@ singleton PostEffect( MLAAFx ) texture[0] = "$inTex"; // Edges mask texture[1] = "$inTex"; // Edges mask - texture[2] = "AreaMap33.dds"; + texture[2] = "./AreaMap33.dds"; }; new PostEffect() diff --git a/Templates/Empty/game/core/scripts/client/postFx/caustics.cs b/Templates/Empty/game/core/scripts/client/postFx/caustics.cs index a712ef82a..7d598347e 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/caustics.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/caustics.cs @@ -58,7 +58,7 @@ singleton PostEffect( CausticsPFX ) shader = PFX_CausticsShader; stateBlock = PFX_CausticsStateBlock; texture[0] = "#prepass"; - texture[1] = "textures/caustics_1"; - texture[2] = "textures/caustics_2"; + texture[1] = "./textures/caustics_1"; + texture[2] = "./textures/caustics_2"; target = "$backBuffer"; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/dof.cs b/Templates/Empty/game/core/scripts/client/postFx/dof.cs index 1ba1a476b..dce41daea 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/dof.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/dof.cs @@ -318,8 +318,8 @@ singleton GFXStateBlockData( PFX_DOFFinalStateBlock ) singleton ShaderData( PFX_DOFDownSampleShader ) { - DXVertexShaderFile = "shaders/common/postFx/dof/DOF_DownSample_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_DownSample_P.hlsl"; + DXVertexShaderFile = "shaders/common/postFx/dof/DOF_DownSample_V.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_DownSample_P.hlsl"; OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl"; OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl"; @@ -352,7 +352,7 @@ singleton ShaderData( PFX_DOFBlurXShader : PFX_DOFBlurYShader ) singleton ShaderData( PFX_DOFCalcCoCShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl"; OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl"; OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl"; @@ -366,7 +366,7 @@ singleton ShaderData( PFX_DOFCalcCoCShader ) singleton ShaderData( PFX_DOFSmallBlurShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl"; OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl"; OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/ssao.cs b/Templates/Empty/game/core/scripts/client/postFx/ssao.cs index 063cee087..09dfa6bb4 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/ssao.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/ssao.cs @@ -200,7 +200,7 @@ singleton PostEffect( SSAOPostFx ) stateBlock = SSAOStateBlock; texture[0] = "#prepass"; - texture[1] = "noise.png"; + texture[1] = "./noise.png"; texture[2] = "#ssao_pow_table"; target = "$outTex"; diff --git a/Templates/Full/game/Full.torsion.opt b/Templates/Full/game/Full.torsion.opt new file mode 100644 index 000000000..52398bf4d --- /dev/null +++ b/Templates/Full/game/Full.torsion.opt @@ -0,0 +1,14 @@ +<TorsionProjectOptions> +<Address>127.0.0.1</Address> +<Password>password</Password> +<Port>6060</Port> +<LastConfig>Debug</LastConfig> +<Breakpoints/> +<Bookmarks/> +<OpenFiles> +<File ScrollX="0" ScrollY="0">art\main.cs</File> +<File ScrollX="0" ScrollY="9">core\main.cs</File> +<File ScrollX="0" ScrollY="9" Active="true">..\..\Empty\game\core\main.cs</File> +<File ScrollX="0" ScrollY="15">..\..\BaseGame\game\core\main.cs</File> +</OpenFiles> +</TorsionProjectOptions> diff --git a/Templates/Full/game/core/main.cs b/Templates/Full/game/core/main.cs index f666c948c..9888fa4de 100644 --- a/Templates/Full/game/core/main.cs +++ b/Templates/Full/game/core/main.cs @@ -28,6 +28,12 @@ $WORD::BITDEPTH = 3; $WORD::REFRESH = 4; $WORD::AA = 5; +//We need to hook the missing/warn material stuff early, so do it here +$Core::MissingTexturePath = "core/art/missingTexture"; +$Core::UnAvailableTexturePath = "core/art/unavailable"; +$Core::WarningTexturePath = "core/art/warnMat"; +$Core::CommonShaderPath = "shaders/common"; + //--------------------------------------------------------------------------------------------- // CorePackage // Adds functionality for this mod to some standard functions. diff --git a/Templates/Full/game/core/scripts/client/postFx/MLAA.cs b/Templates/Full/game/core/scripts/client/postFx/MLAA.cs index f1656fb51..e09f45a3c 100644 --- a/Templates/Full/game/core/scripts/client/postFx/MLAA.cs +++ b/Templates/Full/game/core/scripts/client/postFx/MLAA.cs @@ -162,7 +162,7 @@ singleton PostEffect( MLAAFx ) texture[0] = "$inTex"; // Edges mask texture[1] = "$inTex"; // Edges mask - texture[2] = "AreaMap33.dds"; + texture[2] = "./AreaMap33.dds"; }; new PostEffect() diff --git a/Templates/Full/game/core/scripts/client/postFx/caustics.cs b/Templates/Full/game/core/scripts/client/postFx/caustics.cs index a712ef82a..7d598347e 100644 --- a/Templates/Full/game/core/scripts/client/postFx/caustics.cs +++ b/Templates/Full/game/core/scripts/client/postFx/caustics.cs @@ -58,7 +58,7 @@ singleton PostEffect( CausticsPFX ) shader = PFX_CausticsShader; stateBlock = PFX_CausticsStateBlock; texture[0] = "#prepass"; - texture[1] = "textures/caustics_1"; - texture[2] = "textures/caustics_2"; + texture[1] = "./textures/caustics_1"; + texture[2] = "./textures/caustics_2"; target = "$backBuffer"; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/ssao.cs b/Templates/Full/game/core/scripts/client/postFx/ssao.cs index 063cee087..09dfa6bb4 100644 --- a/Templates/Full/game/core/scripts/client/postFx/ssao.cs +++ b/Templates/Full/game/core/scripts/client/postFx/ssao.cs @@ -200,7 +200,7 @@ singleton PostEffect( SSAOPostFx ) stateBlock = SSAOStateBlock; texture[0] = "#prepass"; - texture[1] = "noise.png"; + texture[1] = "./noise.png"; texture[2] = "#ssao_pow_table"; target = "$outTex"; diff --git a/Tools/CMake/template.torsion.in b/Tools/CMake/template.torsion.in index f832ad13b..a1eeab78e 100644 --- a/Tools/CMake/template.torsion.in +++ b/Tools/CMake/template.torsion.in @@ -9,9 +9,10 @@ <Folder>art</Folder> <Folder>levels</Folder> <Folder>shaders</Folder> +<Folder>data</Folder> <Folder>tools</Folder> </Mods> -<ScannerExts>cs; gui</ScannerExts> +<ScannerExts>cs; gui; taml; module;</ScannerExts> <Configs> <Config> <Name>Release</Name> @@ -24,7 +25,7 @@ </Config> <Config> <Name>Debug</Name> -<Executable>@TORQUE_APP_NAME@.exe</Executable> +<Executable>@TORQUE_APP_NAME@_Debug.exe</Executable> <Arguments/> <HasExports>true</HasExports> <Precompile>true</Precompile>

    1+@p``2z=s7_RV=} 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/open_i.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/open_i.png new file mode 100644 index 0000000000000000000000000000000000000000..1775c78e9ac322c30dfa89b14f90665725baa85f GIT binary patch literal 48906 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 literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/splash.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..b988c35c3c7758dad4016ec8adb0b58c99c3415c GIT binary patch literal 40979 zcmXtfcRU^4_cbCq(MyQlBYN*OddWpD(Szu{_f8PKcf!>}^iB{&3$7Lf5uND0zsK`^ ze}CLhV(u6-bI#s-?X}j9)KHVh#(0i_fPjFls2~eLKtKvaKtTM1h60Z8P5fR1{~%jP zsY)Rr)Wl)lnmh$RBf3E3r4i=_*#f{1)TRm$RRjcYMg)Yw5Cnv4a42vG0l|$E0b$1k z0YNwe0fEG^augvSe1Yz$pzDHwfQkS3L4+^*--3gvu8OL1s54kt#5nAWHkYN~kcO+A zj_YeHTXSkP6FYMR8DV`{2U9mYb9+}8Y7=`iYE5%jS1Wr97kxDoS95(?a~B&|2SB}(#LCSI(%^TFmX0VkTP?4 zZ%(aY>EP<%V(H+>I~;2P&Y^ic2kLC)YVM4n>E>we3|;E$nJUg%m+91_)bBzDozfEBnREVe4VqX7hj4{{MW-%s`bDBZV&-<>v_tkdcXWau2RFd0`h>9Ldj$my<>f~Oru?rS_zjbkHd>EpD|uo#31anF){3E zepgSBSg7UHWZ^D|7r7)%mi_+^%KDR7WpYFlHSd`tMFe<4X2Kyo`HE8M0o}RhTaYf% zo-_hYz0@!j#;e&EWR#*gG&xGGv##V=)iK84&L4086Crm7*0$?&8e}&&x2c&Kq-+vd zjReVs#xb`y<)kpuqc1^JSDMHgcP}Q>{v5RSvbodR;*(h?DSA8q)|z`D3xDH=T=x0# z{k@khm|7Ict`qqnRwhOTa?eS;mQAAF;{jx$#V3uF+Jyr{69^#esw#G| z)kYX|NaZyze&qQ0c%ZrN>FFtWz}GNi=@bJrDcHiiT{WQWWUTlAbpB^(E=|nimvxc^bKxEcqVz%$t|3&iQFwa832wp+C@6>Fuj^ zzI0#IN#b7J-1mY|yS34=tdjexVbwaFHlMtU`vAckhnhxGJ4~>5x5l3mC@tz~YRa$? zyPzlfFJhi$S!IH>@SL1H0Ro?#as0N^$Hibp)7<0kIonth_U$V+6v?xyD6rL_aCyQl zB+P%!Kxl17*$g9PpxV|ybdsb0y-*^3&Oef&YQpdeQUCRm`xmkNtZDT`yD!6*JX+3J zlZz?Y79D)CW<@l;lCvB@#Gx>2SDWjCMAQF?Ookqxh7besZgVR&I1#CQoYOi-nK7v_ z3b~wHfkhDpsUp-+6sTYd)8e94qZUTTV$$?&Y9hu|SpSh1ENP4&WpW(!>7>n!oT|uM zM?+6N#)`8qylk8I%Y4;8eFeljig)euv)IJK=;P>B{)>9VXHbriPSZ0wA^)P+eozy{ z(}OVL;}z>@=V6}o3;mD2*z)At<}YVnds7bGEGo|^b?i#J!50hy-YRYTxc+s#Fmh-k z9BPVUef`8$+kqO_ac_k-L1D=kiavE0Zh{-#XK8C&R9G0&*(rW;ZTTEC3R(I=-$XZ+ zoVGYBjww6_f%u(SG0YYVnT#Usc97x-6J3g8uwq7WNh$lg69p})>j&;nJTf{8RrPGS zwRRLVU$J`|Oaw_Fk`3uErxCH#B{j@#D{a*<6vHqYYW!=Xk`s+N6{vBUnHM1g$X<~# zo#`ZW2CN*~OOn(Y8s-oT*ihkvQ9%DQ|25CAc4oT787jq=H_6s?Ik)IEzX#t+oI7&j zOLjaf=SI{?<;xdsC6ru$8R~fRTZ2B(nr-K9=5e{QF}Lm(8||Re71FJxlU%1>wwzwS z8dW(-%|_C>_79Zs1v3@$af7v3Q=rAJ!r*vid5}au8WvR6=%D}m~ zI&5!UHAG@%jEtyWym(RH*yw!tM@lV6czvU6bI(q%;}zTR-@nluAJ&IbK&oy%DrOB^ z6{N>USIZMu)zc%FkEK}YeI_yTG8)>WrNq61Ehuh2L|Ckv)n9KlNecedge7mB=r$)- z2&g#f>d*G}_9#T$KlMgDbJ-XnYob`MPrzV&8Tuzx&)(!`wos@vjj)J_*Ln(6(0L6_ z((ql_pS_UD67|h_n`-mpwZWoEt62X_8g+@YWD%T{FL65(*CKk2wq$7r?qm~|^=v6= zR9L;#q*%4)!^BJ8yI zS|a?JD$}ILkBm~K4X=$&eo}oBf#K@c`GQCQzIW=>^OfLRyQVl>^al|IHTEC`u=$LS zUKi&y5tMM!PoX`@$Rph=BQ&)}5=2VXB_7iyLpHwrWg1hzGuW1|p2Z(C=6_zdA4r9h zE$kMRno1n|LS&;YaK~JOKEJawS1y{^X_RmFtLru-Gc%KQepD#7_h@xsuHB1gcec6& z2J1Vs)TAF!c5F9K_m~jfZ{4jjIbjxY`im^$vA_1^Y?%6AP>8yg!@3(P4>PN!RM8toSPvm81H+YUOFX%oIVWw&z&b=8kLQ3Q8W3oLok zDW|bNrKXIl3wZEf3V66{Zy#a^Of{dFn)+TMe#XM6nu+|a)9=Rfx~xPWo$6Hn_M)?n zK{?fNOza|g5v30|QufzZlP4J}A0#`qKdE62w2_tXZ7E2Oj+NUcD1_>;ZSG}zMr6<^X}M8ax8#i{34 z3-uTo&77^rk#lW~+DkC&%y+R*&4iw@nC}M9Z?Daq5Ox6Jc=`AqA8}{8;%YPC z-XiyQ&p{}K&cJ_1jRSAAh{x5Cj7`BBEX5r5b$79b_zP~D?{yAj(S<7oEsA)tg zF%`g`-Cc6xy8MJdYbK%-4W;c|&%g2r5n1%u7T$Dnf5h#-b8_mV%I$_|D6;gVj%qPU zYLHT-qR+t8YB{Fm6fkI|-2@*{1Sk}JSO<0LaGnhIeCm7Mi=1b$pkbTAT9(ymv$MCy zKp98UZnTF?6f|tc?FGT-xW2yr-KW(-Acxleosl^DsG-^~M&0)>f!7nLsx*v|TJ9}U zP~gk^>WYfh<@<#IWlc?jXJV(g05TvW1tIPzc3o7;NQideGiPx(_~0NT`R-`uy$4BD z^mJUt)!n_d?E+FX)oAx&EO%6(^9Z}D{dlO|m(_|+8)v#78yovK*ujr$op-y&1h7%_ zd0Z>~xN>@}3K?sW{e8N8f9ZlXS2b4xVqX1{XA5 z80S%qxU8RFIUkRs#|YO6;72w$+Fg^9;cTtylc1cQpC>BN11uiW7(vN8G&pHvQ)Qc@ z?StL;J4IX|-S_9)x$v8-iBBeUf!DN!Kf0e1rz06Q346Ypdkt?a#bZPxdaCgm75U$@ zXLOucTMcZjw$U{o0#jmp-@cTIlFVhQnKiVh&nLiQVYsXN5CTHh9)3AScbxI})T1M0eY6{rIryu^ z^6g2ibct67;x=9KZYZGoPI89ggJy|)3r%%iCm+@&Qn?yS<1@FOg2U`*mP?{ zmDyY953Vi5Psl(a=#2}wU2VTFS+MoS9`8u{$m-A%OJIn!m6i*|-((1EJ&BZNP5Ij} zYq;vsb@QS|r_xO5v^HFt28U8q0fe8PPC4@7KjYFho96vjOWj9a~q+rZZtMj?A_kXxq zz7ggrjjOtzCeboTbOmeTH1NV>EurxVD$d~#y11|YcizxeZRf?ueS87g_nORh%gi>G3fX); z_q+CRSYSv}1WO?%CiZ>xNHnY0c{XoCn%1=-?41#7!q3?A-Ss8SLBEtGk$J9v$QnkL zhN(_fL#Gg)z{_p%?kkOf9;um?LZ+Mo9y2Z)+_mTO@b?R52%x3v7fzSe~B68V20Cfu!d7 zCkR!i-V!ff+wDJmalKRFWo^&VazsblYU0!qD&1fJ_T{3-^7_#Ll|^^(Q&3b~hY|;umHb?s*5`bg3 zeMYxodd0eRA8Oq9-pxLFt7zH*QIvqMFA8pbFVi9?qD{GWGdNoZi+xm8^^)jolM!*q zj*a2qriPZHHlaM%AOv=iO6wa!;{*G+r13E$@tQ1L%Fj=%p`qc4ie0;lb4@KVYt#H~ z*>Z}!6Kz6%Q`2k;LhM^Ti5Kgq1bhMILT!n>uO}Poocz9C#JqRGk{{C0PM#nRH4lN?h&I)OXsjP{0CcP-dYB@tiib?65CP?8X zUpfNYXx8`f(Ay)GtTsFsCzH5+CS0}9 zop&uR!JWAXRpkQ2(Jcw6?RTvde6<&p4$In**KR%Xvm#Wp_sgmPI92 zg7L?7{BvQP*v5}}F_QJxJz8GZimp=^(wGJ8Tt{@)eM1zHvdxD*n31mw_Z`Ru?9J}3 z&%D~2oPz}`x~w4$c3HEAJ~6}WZJ!qG+pq(5*!tlx<+&z@JeyT*MZFX~fsT$Tu~c)X zzuzOeny4(4cN+xZ8Hz%lO@e_Sw+DePPhc+>JgEX(xU`+(xA%gM0Uo~uo z?df16=Qbq;;Rat{d++S*=$Bau?a0y89=)9YjCS?iiT70$6_J+oJ&I4qctdyln9}6& zeHBF`00B13C3#CLiz8UDvF)Ix>A4mV^7KiAq64u?8~;w88V*{~5N49RzP?yt(-gW& z){E^<_NX`cg`VG;jwurunlthIGu#Bi8be4sw(s!mBojkW8IOT6cygO}6^UkpHl2|{ z9wzHaZ?7cA35nLIiB!g>Hn`jH$b;IwW%kSYsF2~7m@e#Qy>zzxaDM@y#Rgs$WJlG{ zqdGvsZHF+6-_F@?({|y*Or@?YEgpX4_dts%sXUFLH@aR-`IfvxVI+p>9_pf$9O+%& z{Lf=H!oCC4R%z6=bmq!F^SX~w@@(m`oZa8u=z=~aNHS1^S=Q8aHIVmbi5v|DPM!0* z(&q_F#l$^1&?Qb5tG0uT5`x2QAV*}#jE^!GlgECfNk1xLp#^ui<3AZ9FvJsI|Fv>f zNwo*rcizzBDEcR1uvI8&UCvBJM~S457W4`^;L~eeI)(|Y4}nim|C9$aaLeC@zQp8> zKQ)7Aj})a-V${tf+-eCbsGb!mBaZdc{*$Lx!mEBCT1}elI_&d%9F&CPpDvZ(KN@j_ z(0;MAvjbQMVq|on6wcbP3E7cd46o7ha^+KJBQ^zHp;!5SRTcX?Jv~2InD_Gn4dyW( z;RFT8Ki?43(l_nYe19|=5_2Fz1zopr4Gau;`T1Knqf+a9D8B$|Rv*M_)qFby@GeWf zC#thGp!FwV&9N9xxG!rdEGXy}tAoaH9XLgVj2hj!QAB95%862XDCXHOmyA_x60p)Z zTkoG`c46@?zQ1?|71qYWmSGHBT1;P7E>Cf!I%A9bw2HpVnQCep1&PJrKFC7MRw}W? z3QrF)j!LGOL@Dzyp^7;tOX;at8sGiggF{Fw3;QFVtnDj9MD58*V8l%LxvHv)dhR$R zS(jr~fxc(H!KP(1*XKA}kl$`z>oAniA*cQ9jXXEeJI;3Z*RiV_0w1tU(EZ+q=kD*f z-%QaKz^|M03d_{w%HF#g(@%BLy@{39fR_Tckd2s*5M$krIpPUK2?PN-VMkqE($dmW zJ81^hQ$7cVqCuMq-zv0uNBoYu0u=FOE=#1Xl=w@s%m|7I^*NJ!qqbBQ?vJmKA6WTg zj4mkqE*5?q;!&6^H+7F{;uNt`Ic#KCO*C7&KIi3X(na)$F^H;^?wl z@S<#4aZGm<#3F-!_ZrC- z;!%%EQ-VFBJWB@$4%QXIM_TomA_TOMe0+RQ=)L{vHRAFv9!|}gJDk`!ax7O9j5~u| z0l*=}YQ;>UO^0c0QA)UZ)!dvu_*6f8E4J?Tx(s=#uBD>%J+sCtCk4)?;(dG8iqcAn zUjn2xw__;Qjq_dd#3$#-PwY?GEC{&t`F*vs^|VwFI=-tP2wERLZycSkA57bG8cCMI zSMYYk#ZM(YbCoMOmR{!Sfz-`Dah?5h|8T)0OR%L9Z81t?eo>CfR?r{4xJ*G=nFCPCgc^w7LnK5WThUqoYl) z&hvTv-#ek`y1`W8JVnk+0jB+*3K|<{8pwBccU^{!RN$qk#yBV;Ufl)Jieqk`gP-jW zG=D5K69Uu@5c}$ONuE1H6NbO6ZWl2YijeQ+*M$wLu$ptHK4bb2x`KP{jpsx5OTeTA>|(YQ-j)&cNFFXl3jg6FRzjsNB?5^E>@XG z63_${ku#C3%l%PMl%B7|_*D2Z)5PzV9!~`d--LH$i{+C@Oa87Ew1Z{fq8(NJTYXOU zw@Hm76&H<3BEOdP;HsUoEa=ztbl*yE#EHLAi!%c#L9QW?itY-H%2l7`#a}h1y7iWC z4D~v0_S-t05TZ?*afkyBIxa5zIQ+X`R<}zV87&8%xVx1uCk@gM#8G(!#L3dK8Fgl- z8Tktxc!|BuS;Cn`ru{v^8GE$nBsd{&@O^DVAvBVuJ0KE+yNvhoZMLV?EIbCKFttyEGzd|+;62K{_5l(P9{_1q8Wr-mc+xOE9m1dT5 z{06=rc)%E(R7MY~lv-j@BpQj}S9t|2nuOQ>obxAcTyWH&>-#021E+t#xCF$P%C##z z0ebP=hNT7ia!ff-MJmN?3*ro?aaiolH|(EjmZ;T$C#!wJ^fI~4ltUKZV{g81Qe9&7 zd~ZR>^Dv)W>s{JcugLIDy~Q>U4)As@hLTwT4OZ2%@tnP74c~qL_clJ~VvXFv@bJKQ zovB=!#awC=G%WJ>=vhPwoDV`YS7()j$SO&idwYPdh^3PFN%e3!&|XVuom40v*Xos7 ztmxvx18{5bODVSVrFK)C{FauvhG~_O$!t+C=Wi(a*&f9gh(rqQ`idVD8=-?nM7@a9 zP1=E*35=wyf)9@CJoxb5Iz=QRM6DyDip#A+X2etW#3vu~Ryk=*ow|tD8QV}$rKC?I z9D}mO@_q*b4kzmd&XCpc1yDO?{z$M88Czmdx6`F!Be+itWIGnD2!RjsgFAG5h zE3EkaE&3hbkWNza@NQ7xJnyeIua48Yu)d{pnKmDE-hjxxy}#(HPDd#B@H(4@x`3V8 ziq8-T+N#Br;uK{ByM+(E;n<>gr;}4tQ)|<`K1PBRALeS!e*>EF2^;_^D$rTG!Xnq> z@uyz{*zg{l{Tp;|q~YDC+ml!Q0-fED98glzvcFHDRp1g1$A~5ETx-Nw6t(Fnf`#L^ zR8sWlENfa7hlABL3GZ~pq=(Gd$r3M{UKCJVF%xyQKMdD`AVMwE8nR2XscH!a`1j}} z@4;6xmKyQx#0L2p;>PNZ?XB<-M(>1hcPDRNLCr{GGFm&jzdPX?vhydn~QaF6S{ zFT~@PREPG$h@g90B?`TOvoT)f>2uf`A%4ND)Xg?R++#&!Ks`z-;Gg!)9h?+AYtP&~a-++3_Peo&L(Qcbr1RAFjsg99N_-Isub>lC^x> zFV?EYAyf9V(`J_4&~qg$xV!4izrktsbtvv{-;>d*HS7J~Ext$5M&E1MKqHZRpL7Y%EJ?=^ z#p4JqR8+XwsUTS}T2IfnA7!B_t{e+vZUfJ{xXl&c_tXldDDp56cDqtADnwke{f7RZ z7r>qB*h5?<4Xx~&Lew)pg33Sr=AdhtK@gLe?GMMpF^6uKzgV059_vqIvm!sZVTZ|K%WYz?2V}I%rHf9~Z<%~H`eR+w z70}7?@oy5hy%OVa_+JsB>#`tia=V4bn7oJE&BsmSk@Jqd>(-f=P3K#G47u0EqK847 z;?n`At-ZZ<5QOymtvN$`wy%=IYvRznYxx^cv2u(<(k9Ex~#O_2M zuW*K}Iat9r^fJ5|3Fu>Gp6MRweOv9r#Q8e5Z>qe3bx`87?ZVd6lg}uRaTY)Jq(eOP zJj+5Lxs4;RNX!D06gSukZ7C)3-<#h1=wr)o#?zcpzXfA^UB<-ZfuL=4*+T--B8s$` z7zuWa;xzmp!?q+ggJg+)+MPR798L`DbqC1$6#1{(!778_G-B}=;I`*oK|jBkI>nU$G& zKYtLMhCHMaF3)31DibJPq`WM<{~gCq6q)N6ZJQVOJq;7wNd;Mo=9(dNA8F-=_-%k<*daeh~-{IT4vLG)OM|kP9|M|dvpj+KTWcGVGxut29l@4Q(&I6hx+XiL(r zao&;j7))f)2kIz$_dK9p$@y&^70qZMYj*)29s*z;KamT>Mzgp67`5pMMQ77%`~*4> zu>KRl$ioY-tpyh0@SbpNa-bxA07}Z!1%bE5JMUxOwg!7HRp1DU;S6sR* z;UeQgVW~F{O?FwJ@}E=~-9?cY`6guWSa1W85a{%pyu`qZ1GL47nVJ6X5Y+9R9cT6W zL#5p))va(7P}_P6obd$;8Cj=YAyNBqvMo)1BqnkCvUJN{H`fGr}-UM`qDd-ebGI=9`siy@W zTaK6~1MA2$J2n<)etzE4&JIX_fZZJSH@fx#DnB+k|M>K@oFK(d5g`}^$oY?~ZF%5X zU3tajrO8*!f@FAuo3|&vUot;E!mkY9{j>jx_Oqc;@I1PhC z*WYB-$H8EOiJT-HrX}RRya(mLJtZ8uvl@&!W!%?LbT9FWO zLZ$1UhUiE;%89CJvZ}sDi@ElK1q-PwPOiaq#MCnqbHXF7NYjJ-$uSL{^Fa=ADEXwy zj9b9nGw{mL1PilVgs7^s+z;JMET~WGi~zNDpep62^L8m?C+oVQ`x__Lg~|@# z7?}xaGgRI(1onP9kwX$pUnU}pmZb&k8$K2a(78^}&L$QYRk<@<$3$n}7#WRS9<3Un z=hx=E6c_^%74VIqDtwD7(8-LFE#qSX26qm)kUkg1!?#S13S=RJKbd0>K*=mO~eSqdU*fLqYSNw4QM_UG4l`oRuRl#a|sxg=!OHn*eXZHe6Qu9P-e=&L!YFrJC8IBnMvSRxlqc3Z@Vtln5`>W4~@#h%G?zV zk=|Cy?3kaGYbn3_z2rULsOsS0uyp3UyA36wK`zr`RfliNdq+ue6nY=*v?V8Mv92dB zzqbOmQDh`9`n?IyXH3=<_l#tP!)hmfbrE8bSU`kjP=9%pIw?{|`3z&-uY6sQp zsSV?Nu)o^y&Vrvu-d?>cUw1Ddm9|U#hp~1wiA;Uw9i}j#`zE zCKJMyuai@y6Se>$EJ#rdE;22M#n}1T=&HiZ!Idr!!CNFP8J{A;Bhlg)(E&r!LQGiL zU|7LBBs7Rn8I|-?*92FEm*mUpA4Lmt7#Og_2ZJ)va-@x0zpJ~jP8_R~p^#hKv{Y4O z)h98aVh7>6$hhen%VxZPZHX%-Ej_+NFGRa48)PUcJZHKxL=(^}hvkM}cz@8be{o}c za_oPx@RZo6N<(;kO)UFtYVswYm6j5uV;ObwwJ;ZvG;qHC${392x9g9i%ClJ2j6%L@ z(5BH=^sZ0iUk`J6TZ4b(w9jswxsd)7giJ-ahMGNT2iHup|kq6OlU zb_#4wa$cQe1x}$9DN>ZWPYUd}E?qw1@y1Uqeyp)QtxJ@6N<;mmrD6^>443pn=MQ~1 zX&MF#B*exTvfc-aKGK)cDxAGj(Y7@_@a|L`Czi7ABB{(zDg0tWd|z`@^S<{gd!n{A z?;Iu_V=;6%pOCBM%CJm2&bpMAr+14(?K|!B2=7=u)?4)0uU;lsFRy7u-D0EAEec4m zM)%2Mhj?+U+Z&_Yv<2@ARp*WFUSuchTHq0h_c>@nS-Vz}9m{TCw=VA%LCdP$w3}bg-h4<#_SBHZ87}P8b{Kg-BO|`0XBDi%(q{qq8oEu1$-+d7K zqzNA!o|Dp`Lw2M6A#3G=yiJb#uIY+-%FC5y%JEgR1p!rKJ5|7f?oCan>iE}en5D^_*s0d{_!@6E14(QpS>}-w&f-9MmcFa{h z@-cLn{Jl51ZL^efsL6`l&*dc$Md^416;#%VK37HlI}3GmHYutbXK*mAEq=c>C++x7 zP-Rr{Tq|=;)F1XEap)ahzS{FCMMYYVQF5kd-yA8PQw++*ev_)@idT2lzQ106Px>#K zN@YHU4J~X)J2q!TAz!CcX0$Hdx=U+G=9P?4(m%&FO_g9j_QN;yOxoE#Tlg&B#0+tN zBxz|&3lg58b(yal#_NaGb19Scs@l` z`DWaR7C$&Q*D{vCV3NZvW0*&vyNPRdo@I*Xz#y3{+g#1l&9jDd_V?;+<{dF*Rdw-E? zh#TwqgEO4X!q{h$nUBl!fTI|t+_i`GG@wiL?wCa@R*7l6yWS1PJpCbLk7MbA>-Rc4 z$@=Ls>raPIsUGFF%a1~rnZ6%uiU#6c1bH@tvu330*X&l$y*1RVJgQ_u4WW$7IjmUs z?CQj3m#0SP`B3c20Alo{uY_i@5_lp1A}QbQqcvyiQd07K%Z?H(L1O=tz2X-HQ7XdC z{;7FiK4q3G%SnsXAB~nLmx#v*6{300g|^lXXc?P4OZ3QBE1w)o3nkKDAYeO4;_es}ze-s`!F-`x<#n(9W1+K#{9K=se{ zMmHFNLYe3(uXX^307sXw_pqU2Mg*(eX%_h{{LAO4mEc4Tmb2(N5ThQRmc20cY;n@-&RnrZ(O>g}@4?)E=sav?LYovJ0}v{OJ=L{SvBaAOW@ zG~Up$R>H6+rfD3ahX@O*r6LeR(09$ceX^k}_*>=qd~FHR^5I^S&_l`x?N8&SQk8l0 zE7&@f@I!Gr^YeC9nEz2l*L46_sYOR)itwrFAJ#cHVs5HK@dG=a1HZocwruXwu62qx zn_`_~)$OpIEkBDCd+}4=(^_o^v_q`oyjB~F2zDKtIlzkD%my3DdeFk%s*^&HM`D%W z+lT_vPE^}d=bF~5eifg8~it5UO1;I{qMK-z%<07T6s@5EIE{q_WLtM zxMG(XgefTON8a_+^zF4Js^^7m&XV4Q=LElTqCH9}y`?0III1##lyNij5 z0X$G1Ad)xB$}v?DyyqT85io7z(0RoOOgL56dSmKiJcJlE*3+zyHXd?^_U?&7d4pQe zk){i|Fk+IhpPuoa%s|nC>=u9>A|#1PeIrG0%mr8}M)D-`!oyJnepwap!jiwQIst37 zoR5zPAOyV*%4%v7lsP)U^vDRX)&hk*UQhi_%yU=VW`=EJEKi*~V+}ZoU=0mKle`%c z0pfr+DFCDvXbB&CEYKfDlW-J+Ydn*f^PM*1+Cx&Hd%!Y2*@ z(4Rlm)N(}!%QQ}oUnG0%q)V<3b5_FRn_$rW3`#?i-$6Fo-j0@5NNg2=umohU^zhhY3K1KMS~&q4ZiyXitCZU$o>z z@wVD<^vN?(NqKqHfD4a+0kGF1WodnZ<9GwGoPQ5jdT;0Q?#RH_eDrQs2i)!Q3JBBz z-htBhP>OAsH9cafACM8i=$)nK(9vM_qOG;n6L8^=qX4>1_Ah`9G{Jz>Szn_4KbWHW-XFc8!MCp@l=yTkwteU z9hre%j=v~+ljY}+PZd$4cgd?dR}X2D(%(p}4$U#$$@rN@scp=#Nrn-UauD8N(c>#i zzcw4ApE+^vLcg2!KeKPtT;C@U8qD>NJD0^6ERp-zWJbY)xzOvS9R?Ut(Zg>~^!=~a zB?Ase-hZ{rauwctX?T&?G*@Tg0-m48v>yxM(cs#3UJPKg zE|AY=<`7Vij+cik?G|ouA>j9ZB&y$NYCib}%u-&!-wlYK`j?}VyE^5alDYGJ>TU(c zZy{`pPrk&DX5t0|y~t(wRnu2s&As1nxDR*RooQLu-1|4$q@xFB2<4>vFf^%Biq(Fm zAlK__S3=?CO@1^WPdPmbj=*){1Xk$e(GOkN&@k=Co-~-8f^$mM0-KcyXv~iE2O}GR z^Lb1|_xA@6xyyI^9$x%)8YxI`x!N`El;R$sWf|emt6}xT-np>rqn4db!Y8a^E3S|45G$8)!%Y9b$4uw5b2V%^pO+4NPj>o5xTu3>g2<`_TaL zXkN!MEN)#=iZmQ!ES>R==!;6-y2~AC*H9vZGJ>GzA+@^19WRydAtGqL)7<>!-@ZQw ziFa~gAuFxQh!V`NkfiB5N8b4IKL3v)1a`K6^9{oWo*i$NwVwl#`*8?lzu|E~cfT|p z1B)j0n7`bZ3ou%OKh*E{0i;~n!U@DRl3IA#f&WFD?_oD;=cY+{ts=`D5Wf92tb}4$ zz4OJ6z7vR^jPL;MjaUoT|DG!|E34(?r-+rU?HUl>*Z*{f0QpWJ4lAYBbYj5}kFJ{V zEevJ^{SLV^WSIh9-Tn2O9Mh^8wUW7JC%Oon7suKWo-a5s z_Gq;;!%gDVO0K{)fp9MWhA0|0*kj2nVrJ&;W2yvep22H{4;J$UrwNfamA~6@+kiSm z@26kbF`T3g)Wt_r+J!)M-N2)ocrq$*_3U9qV*K~-5xb_vevn)H9;c~X5M>HsFopv^FYRBz#0p%k-rnBL!26OR;6MqIl`XJT>5vq$mLE#z z*olzdNEp1^-@B2KVj*&bk$-j`i1X0LLAmx_bNes_3sBnCRMSl}qe!jJtpQAOrp4TfiblgXOP%!xxRq27;VLFl)Op3w}zd=B-+BRM>{+Rtlp_5sI zF05slmnG*%g2M3+BMC*9>4win-T&qhHnK&Es1H`fVgC?ODc+@xL6Q{g@eoxqR9nV- zIABEpVSq^=Tr3pecEFUlo`@54{DT0dCHLu9FTnUpmG?weF}oc`nDlQjXaLsoMW98V zU+QD@0md2+4Oyq={ZlYuROlnvyiA=6;Rfgi@Yi5puYL=90#wJ}EiP9h7UD@ItlC8` zK;ft~?061J4=@aZ==sj8%HFy~3mj6Y^#GM*1H4u3@1LjSzId&saWOwB!CDPO!#0cH z@)zq@3bO5R`7`lX!=Fn^z{tePGtt|_UNEc%?C}+EzkCLU!W3Sa)K9y9V+;4!d=gvM z_S(b;7h0@+K>WX2Jn)wXOk1(lBTElP6?M&8qfSr@_r$_`SEQ&OQ0@@(QD2Ik$_QCc zI4{(|Z%)0cAcSQk*zyZk&xWOY$Ca?Qir;wA0XvmA$@xS|6m3 z&?nh>`7bYbyHHO{PV*LRYm-L@k5yNEX1~?8I77Lum0f~)pa(iQ-|FG$N4P?DOD3(E?mbKbbEZ*z5F#^U8fjT2uyEg+(6LOYpd)Np;&JAa>Ml&4($_ON&xg z>#Xauv`e#3Q~J}ZKXZeL{P5MH50caVwm;ycvxS@z$D!n3Tx5wP<*{F0J2-6cW785& zN1A*(`4#cITGGj&EtZ5-muvNMs`~juN^8Bwu1w916iey9LpQ+mpwU!AIcgb+X|EvfW8t-rdNV|MX;8b@e8&J<6hWM-ni30u%(W zmHBWgm)nlk<0R4vD8t|U+<_NZ-)&kOSRw($^*W!k$Po47e)+cY(L4I6AAy;QwP_YG z3zkyt3$sMS>*f4m#w`lwlz{yBS)t;Cb`nt4_3T^1h+e*JhKo5Y79|6TyFd0tv{fe1 zD~`4bckkXk>i=N=3s|**>v~l! z_0fS+eZNzUQCz*#?r~t~zBeyNo8VZzjU(7MwNM_g1jd$tY>E^3*h7K2T!A?#moKS` z(epj#CN=9$txzE2W9J~?KyM{d>|u9)f&xnAYAL^~n?j(@C2&ZnMgrX;BZ#b?m9ONS z?-In^;?lORtcq{*kKeHV^%NDCoY%AMe9H`bevznd(6gO#T6U^+=V}GJ?$FS^{a5l* zj?3ql^FGH#XKj0+4Mju?@(&16LR}_-@Y!FiOlw6a;kkp51fTy$*O!Mw8Ge6{LW`nO zA)!@Cwi0ERB70HxH7c@Y8Ea-lQQ4A&WT`A=-*+Ynp|Tt6AiJ>+W1ZzakMHmIUf1`% zuJ@lb&)m=b+|Rksea`2sBi?efZgLWw3Wq z{FI`kBbDqB_q z$g|A&UXYnck*Z2K?*l+H1DLJ3%JqN2vjb(3z$N(rQo&mYWf(FWW04?&S$U*xfL4T( zfuwKW8hOO`7CUtG_QrbXrSZ8*zjfjN10i{U0(asRQ%FpZj0d%{7s54sc70mYom%_U z=Y9B8eAHJqx6}4Kj~R5?qr$-R|LD=9#{6j=r59je1FToHvic%2t=A$UE{*_j$BH?L zeYF-KP(ag`sZr$oIfWK<9byE}`faW+MZS+xS5P1HkmzqT_Hc@S9i<;lfzliO@wi6o z7+D_)P#6D5%_1Hh{e`ydIaRC2g4p=@kA*_33RBV~K6|rrIx06RJ&5+PT5n2%wE1yP z^UHyaCBhf_eA=pM!NdKCNdbNV#-xghIH;Xi0<&Ulj|k;VTly@q--c=aaL!F8 zIX1noQ8@J0|8$$^%dZQ;cR~@16zYuN#DN4JLvMo1f`kOBA?2!Eg>~AwA98-YkrK@r zJ^a2}Nac)0ikKDN7s{^oSV0B%Z=T&@ql*y8hr9(RB!Nd15e6HL+9V`r6+n9Bvogrf zgLp)-gDa$QCAMO|3_xSPSy;{mMl2=}Jb#|6AH$qUjEck`TzvcPB z=Cl5owV+x{1ZqGsJK&Dwk`l>qM#xEvPO`u33}B_p-%5EMyj2T zxYY_V{L|qwKS1`Tx8p7-Gd?~pu;VKx$I{aR-274;c1=o|rBp6{y6 zp(f}!>oQXJ<;Z!N2gz?Xr_ShaOlxDCz?`>}WDFNpT~TXV$v+%PIdUp%^OevhWxB`} z-?1o{k-f3!(F(C#Xrt0=*>im)$k(bh8(bGonB};qBP`|?kSShmt)3I)!Mn1TTTM~B zR{QfzR@|DI{77JV%feXdTT6>Gxkk*+){cv%eSIJ2c}I9l9`254*%F^l6kz1h3+#4# z>-_A5SjU;i%)3uLVPq8BZDkRR(Xl<*IQ-_2MC0*S!Rm2xwr!I=D{8@J4WC)!O5;xo z@LcS7@2W9$aGtLlyIXbXQQ`T6SD&E-l-?ZjFFeq5{mDfgnKuOoexTo!4*Ro|j=fb! z9A|*lUGDv$lW|Z~L|uu_1Gg1`ih3mV?3N3H8{$0(kls+|e4H0cafYP((bq}$q@dyz zDeDEF$7R?FkXyBoiL3(omDfD{zDDxj&x`SS9AL?bsEkcwjw{qpiUQ(9${ry%qX~^S zRZ&vf$p>8oa-e*5wdxo%h=?yxeZh74AuDN*n+n8;4NjxO63f)AL}j zoQfC*Z_Q3^xs7CzPo4eZhxa1d?1lVBr2NQCM$SVJkCk-V77|`35dK071ms}5-i)6pf->8BlR`3(nK~R3s zr@yZc;d2%>(co`q-|g+mXwrYs)?z*f24+gM**{3ed$N_r*OqyICF!`TURldN{6f?_ z#d8MZ_6e0#=QF#Lj$JNx(ACwwe35lu-LT5T3waEkdn_(Q-3N6b1BV&h4-AWtFT6Ng z(9+)5*940$oh$A~6e;u{Xpxrgl@8x?(xhL()HHJ_t)wFHq4G&+(eqPh&#{%l6`a|> zuk0rlR1$;lVQ*jehZN1_FMQNrLbbq;+}&BmDv+5Jd{k(=g>N)y;)I$9n z#{sExmk(2m1Huj%q^e3CIAOK>(shehvqK>Vk`J8GR8-?kcELM5m$Kw^8S4^d zcfADD2FW}In``14*{=g~id+BWa9pYs(=^3RU{UUT+#(o`y zoMR@ynaffEGpGuWg^AuriR1GX3rM0BI3@z2IUpilU$}`Z;`ua62oX-0bT99EYDzKAAYl zH#kH74Z3US@hK_DKib85+ycoD0|%Ii&s-7YHUGUgxOK3g$PS+m%_UCQ;a6*R@x2r3 zYM)cNTmItbnwgT-c4r(iVf{-)QhJYsGWzU6O)c6XEi- ztB}5hNqF6me^H~u8jNqGP?#}aXXsIaSKwfU$7IT#vzWf8*@< zy_WqFH1j-4r-9a)L)p}P;jH7r>ruuBV_f=biA8Z>oKHyNrr+Wa@r6Lpd8rOae*&}m zCuL*Vi%wnJ!??rItm&Z^{>}Ma2c1y1@u;JHzllF@C9d7WZ2Fq1>_5o}MG#PXPG~ef z&2sQW{23{&)f=e`PbZ}n^ai(jTQKhdc$ThJfp>>8`$&Einwg|#WZ3zANzrnozKrAZ z5@PB~DAz2Nl&|`;vQ;@Zb9Or2mt4%*s#`T%))t|0R-yh%U*wdF+_NMW7VcLO=sfbe z%L;C>^mpHE*RPM9-T70$4L&lnevrL-iDOhGI{)PN8y0(}_A%w|eyj8R@8L810tyc# zH@=lsAKM+@D&A_Q`m&_v3f)R+>{x@Ss7wVjm5D2`Mps+54qRl2sf-A>SP&aIm88Ak zDSJjV_{7HSSLQl7@y)f1{DOtU1w>k@q81h$!Y-8crOy@5hA0z~nVaFTCD)KJGh8>Be(I3qP;-n^fnD@(iLLdd>H}^QIR;XGJCRgQz~Hv9Dcw+9dsY>grxcoIY{-N{de{^Q zWjy42XTbTIx{~9iNl5ns{4jRTw}j5;HFmISrCr^q&?dqRG-CALp`*t3pTH6n!OA<^ z`;?iZag=e9BFDk<+|sDhG{2$6K*=dK+Ay2A$NYfxi7OD+m;>Ln)NF2TVV9oqbc70T zTS1FlhF01aghu7yej(3$;Eem7a8(Fenfc>{DL{I8JO+Ezd#R4&ELp1C44w`+S(4hu zM~!eFa8?No37LY!EX?%hQ|?*kARhNtct!>oRI77>X&uv3kR;au4*T0uT;LaSmzqin zAo1;@8Gc&g#;Vo=XnLtbCCgd!s}$6LPE~ z>-my1%)91Fh7z3cky=s$3+L845^04P+Hzua!b+4kIW2=@uo`OfBmQ0;-3J=Xaf6~Q zv-BLX1AL+IqZ4|(ESE2K>S}kz5mxx)y(VI%X1;z|0d|m1qyducV&X7KV%NH=2$2Ds z1U!^(=sb!?>k&|6t0Tb1Titebua9i@;j2@T?HTewVk@+NBDCuZ-uMbq_Sig#M=Hoo zg#X8NEMj;0VOl3SJ`V9oey~dUx9Uw_tv0(|>R9P1XV86AFZDT`=mNKs!;#Ke9!yN3Crn;VO797EBp(>ua$)hzt<`Lz14 z=3>l#|MxP(uRs2xrgkFGT59s6E70!QF5a);iPNu`FSjEEnf?a{k(dZB0aLK!jVe9!iU-||dBBBU4#E?FlZP$z zS!#@4ULOr70?!K$-ro9sxn&A~Pq*s1_;*7q*sZw|BD8XvCI*f{Qg;{BZ4{1%Nzb5j zd`tyI4N9MUqOCg7^+dqK{h&GROuTbFS2^^N_&gdqP2T6^us|aB6ILa7pvgj%(6f1g z;cKoFQvh<>p;#nYh1cuMkR13ETES#b8x5D2!-`HO1+cb*zlXFptA7ru@Y!I`5?-f4 z9hdn97RwwmEk|4x`Ycqf#I`);_wsk{A3i6)@e24OM5gqexBOnY<=B=R&A%5HbAeqd zDEBakkU&F?-D5CHd>>dr&r`SWZwFUl+-b5-qKj4 z_6&GOI$;*tYzs{TNSPrAif1N~Hl3&zuf0>Vi+cMBLlTR|kLDL>+aTK)Yf<|@=zpt@ z>L*8CyL9QTytr6zHH#^sb`xoVwZS%)?!!=fG$SuhjK{52abq3PW#GAMG8V4wJ{`@5 zBeyWw>nx1#aPlP@le7f@^1O(7Y5}TZ3Scdwuv;NLWr-o0^Do zdM~DYF7atToZm4WDWqU!AsWgwQxbev4OQDK2xMX8NTk?uTr@~hV!d`F=(x*E-Pv3HN)~R%O0; zX5OQ5+`Vzc{WQ*WDOYjdp;ejCSjGL5E z(c`GU^7N-5@T%SX*^HeBNH{um!4BkVr!--Sbz1|Gew`*S*0K@zJ9#Q$MCBovobp~k ze~*bTxyz{fY1t~l$4M4EBjqE3+$8k~8p$L$R!q5KHXp#U(OvJpZfuvy5s$Ky{F$Z2 zy$`t^3jRnG*=Czn8LbVth9kDjAjG=+N}a9DM&#g9Ie1>k5K2PCY6mnwTLCep2Q2L% z5W2MFEK6G--2~rfcAR6mD)?(7!D!Mg*FSafwB;K_aZ;l7qQGUZXKbH$)0QAq+;hr% zIS-0zjtznuG|IdG0x1B}Y0l+)4&ANZZ%BX12ni2g8_XFKF4%~6rl4{`2Z9~4eK6bF z?jU6Hp0hL0KpH;vL3t#aO2)6r?%%Bu$kI&-Vq+eOHpkgTPLI7GH=3gCGPs{38O+3) z=T|$#I*&1-|2eN|BtiY|h{&3tArL!#F3m#fIiL24lx#+~wa9JRBaCVUtXp4SUkIL_ zcz_m0xfai8loq;4$$*P`wajFCMs_>bqmQ2eaJ=)5yh_Y0aD%G#nMd+lv6)4ESUMFA z{$Lvj8%oWWj;?^oatkUZU3n>426@-{Hh!rTg8z!g^|zSR)@;F9@?W7ZU%vE#+(Gx* zrm4Oau9&@q$S#Btpn*?1C};yvjuoJ^#7$lQTrnzm80rqeESbSvM~|)V*n(G=B_vWo z5Nd_Xt{Py}J3(3|w_s_@$g0DnX3isTkTQql62yDYd@HArnv1L(5BYBO5B+W7+Lk$B zW!?naq0{hDd`c~=v#I$-jzx;k zpd0>ab_XwNEO^hBChvD~c-ni0=gYzUwwfwNkwwa!8$uyp!lUrpYLVp zO*hEXoL6ofZ?0t#sZ2sr8W1#8Xi?)&iLZpHaI_g_<3SbB^o%F20HK*A@YYl5%A(55aoB=kA>1v)_C95-eX)`-(*HAo`!{mO)S^1>ZWzH zDY3~WBaq4)u-Luc&r{(2RD|esA&^Wg%cv-Q0dvDH3ryHFB$Bnc6pqbIQICGy_W364 znxfp`)YMnR=#zyqGH)mqTy~>$Lyv$vgWwE9`lGA>=;OyqHzDW~(oJM=d^y56B_$>A z*}Mjlc_6YM3EndT4n4V_oap5XP%PgGixHHCfR*=3n~IO-Dxu>INR*G&8-JW;z`M}~ zM$z30kz}*xrA;!iYue2X>P{X5TFcPS=>}{Q$&e(y2SR5?P->dZ3uq3J86PB_%m}-Z zh$;sP@OSAbPF~)578hpCAs%Q*f@A+PdV)CletMT3jaoD!YZ`{-`(zj4g00n~FBaM5 zQ38i_6?6F(5ub62GaVFns_Wlt;Fq&P&K`n`sf~%0SA>;RN!1yq+X!mHaEIV#A?aZ{ zGLd49F{H#kO1P8O(YWwn$TEn>4MOp2DdU^TgSKh$q^BR@0B!ir!zqsqyEYM1Ir0GB z<=n?7{)j1ou!Iud=Iw-_23{YH1>Xf9GJw6i$O!;kkp&W3vW(}9ZKCgL8{h+@V;-G% zRBQpB9pmG}|B)QZ1i_`*aNJdc_g9ulnGO`Y2CWWZ=`z02gsL?HvL7Zo3m?6qN^zP! zas7F+S#Z3VvK>CVOTU_QgvY(d@@+CyEfK*=>3|}b#2oxC`SqG=aygKeM3#a=Yd6U9 znDX1n+6ve>6L35N4H2pY1_fP#3a9q)(5bT)Yfcr;8FLRvytK;tO2SU|8T9z2^d&;Z z!0U%U2;z13Obt~;9kT$8fQvqwzb!nhxVwW07 zADI0CO+8hBBXh&|+s3((#Wru;m3Ch&5S5JdGcuoVZG8hpSTq#~wOWb4Aq$2`&zCAe zxcdW&NLx7i z7S4NfI!#a{d~>?gaz1(n44YH{yjKzlO!L6I4%L~8gHTsp4yg{@IbU#mgO~7dQs|JD z(hU+@vQ@&B_yo`s5%8AkxE!fDSgdN7y~$1fZ=L0e#Gw_NL!KHcM7w(yV!!g4V+kU5 z*1XzwjGNQ-TK{NL<>bdk0(hp*kq6~ZVxkHv-~ajv%X$R8Bs?*A+?QHwHLb=`N~Z{k=w_+x|WmG1v1l|eZY9oIB2-uv*I1AE3#;zUjp{=^24HKCj&+U6kf z%Y|7$nqBtW{mpw!vbfOqdFkWjd|dNyZD)7c@mRg4(J8_=Q=4uS8WiYiYW-G-+i&#^_}F#^?`)%SUh3)2Jj~0db3;-t>f*fBPFPtvpD5i39ad+Uj@jvpr`D zeJwVrLv&y8M~w+Z(UcsrX-^Ps=}_FO9Iv%7cMnWdH#yn5)!R!=RlUF6q;m= z2&8@d41qf(h-$X?57FFxr zkR)#M>eO`Jm~gl>=$6&t=7?)dy&M5>y!mU~qw?kf>n83#+g4wh-tHTAK5P!Z|END)CCX`~7M+r&HsgMX1I5b5W*00}+j&Gm1daGAPylj#xxm%l~gH6Yxoj<>g+U zApWyKkXwCVqobPy(x%?&YE3tn_~dLtOS7#Vmioe03`twFB02qYGhx#6Urlo$LC*;3 z<^v)aawXBfP6kZ@%((9+B~O^KV{oI>ez{*sU~;WWFn!H08&@@G^(9kzoLn~PFJIzF z7}{^9{6kLxcaNVe(lztuF%N@Z?0qStK#4AwjP^Z26%hJNAikQYVT$ZHXjAvT>$$YN zrasH#$FcLri3yB@VDFI;OC}L;eUUf$v1(QKcGr*J0XT53s3+1^(#X)IMRR%LPe*k1 z%2`A&Y^=O>12dCYIvUmi!2p489%1I|HbY!L?q zB$KT`%+&~vMN^t9TWeX+RV3PD3mJ^yZ)rztl6cR5uQm~(*vE|c$^J?aFCq@=6us}z zaaVO4m_qw1G3=chu(}arbgpvwuMzAE2Kt9iLCQ+EsmEZ-$cQCkOqL_=lW7DfA%{(c z2Yr{&=iaKgD=J}g)fihb=qS*-Z2W@yUJqxrq^ou9CYG#^?J6-VA$VtOuM0W~c}XGC zd0JD`ktYmnNyPdXS1tii+rOFfnv0veWBu1lnw48vaf#BgB`{>2=mp0vDHG>PF*2+E zQuRk{bPJjL{ykniE|?{*r@!w3L*iOtWIv~+tL?rT9>3mnTfvSS^^#?q`2v9zTjb&) zj1OW)Y@BW6-&jFcm{!3XctdJW5tJ8HF0@ZO)`g`^&ZqT1&d6Gk#w|-5fr~FNFfdhI zG>hf@;GF5a!-@I$@AJzUX5{orZ9IpWry}A=neIM_cNt$h$VvRr;PldY(u1zZp_-!K zUKVq@65l6e=jL`7!q^>%?;q*j;Wjja_ytYf+HSt}*5199^PI&vbg;bCv$hCTPIg|C zMV#SYiAC8r#qBNjIz?wxq{-Ol?oH%cOv^bo|DMZ-`Vnh*tvmb+j)n=gA38e>J`o42 ztYS$XE<=Ztjck9UlrI&3u~fC2Ee*83V(%k3;mFOSpX2oXpkSz*_aIK@L8i6T+f9)X z7y1Fz?|qR~l~MrmDKy-x75ps)4^MEGBTMF5eFgi zr)Afj+1TI&KG;T-DB15uY4%8ztR#U=nW9j)hdbE}Jh#Np!NTop)oZf8&;l5bxJ^M` zQUM>KT0Z$#a=Q#w$7E_5 z95Zx5Qc5Zvw-JYH^Jijl`z_Wm6=M}9AoK{rADaV{a?$2}0U^2lMi^OOb?q&zqqR4mnevi8j7TUbQ9ppycI}hT@9*0>MFYHOrhpoxV?e zJG663D&shET6;?C8ydvr<~tA@`MWAA@#$42RO{s< zHAAVp|MABK`dEDDlmF%QBi`xe$z=_j>Y=+I{;FR+-$M?Y-#o=EVKh>Du87 z5^_&VEp2RWoj!efBgIV8+%xg>(0#)rhwl{~=+4rOS(|3vVbvV}diu9<_B75l=)h$1Xsf2c&;<0x5m40bdk!mjBn`<;^(m& zBRIv5n7z7r!g2S#IU-9110k)btEb0cWpE+S;PX7K`P;9x)V;LSU?lgIr2ZXTF~9j* z+<9#39oy|cmX<^gj1Fg?R$EgS>Aoz1LdC>W)N5$t!;6YhxrrA4Ue>r#`8yy`G1lhZ z6aD)}87ep3^^yBA6za{UlRC>#{73Jv3<1+-RCCG4ZuWOaz1(Dlr4w!rb*pv1=R{`nrAsBWTX&&Qxwc2LGL&W@ zO;?1tzy`nCbvd@Ne45CLt~i#Sh;d=jC2(8NY%iqpST}anGRNP~XEywCck-ckaR&Dx z?hfM3OIL29F&f|4n$m4Mygr1ud1VS4vzxA3ET^Q+hsNF#W{y91h1zM)a^-_ZI!VUn zHTuD4Ouxp{-fvzX{>sKW+}1DuVI}wepLO%Y0tZuEO1y!QuI}3ELR|S^;6xdE4V*xJc2)u`5}4op0JyFA38hSBBxYSfu#*Eb{63TdQiX z(vChpr5n@k+2|RU@$vJk-|WUUr_9H%S(OP+{u<(2aGwX05EjgRcCv^+bU zV~K6bdmz~uP0y+v~S1v9Tj>Ymb+W z2c}4w?QK?{m(Tp6W1>|hQ#38uu$0x8_$AkRozAxQ@Z!)XnpN5@K{;$h!2L3+?Yk1C zQoZp@I)tFEv%fuUdng7Pd`B!3n(Yk#+IeQ`cbQVRa&XG$qpz{QYgxfei|~w74dqVh z*uIM%j;`$CT}W)c@V>?1Ll;puyP?T;jea7y>*~1dJ<~Pv!Y5k(N24Ixl_CPJhj$)w zzlsbD)uIl`J2}QK$wW81-f;?237gIk$~3w4FMKZNX8PlEbRQ{$;I6OKZ>3*8%U6*q zEoF5yxTQrnqwK6C#d)&H_|}nurPJvq5}LLG^SUt$PgJ{7zQ~tMk4#q`HP)+qx+%b= z$`eca{<)p3HI`brqwX&o>yuHwyW>sYj|8kwn|;nYrP`^TihSJfV{M zUj2d1!TKl0ZC^(|G1wFM43kbeFH%yuYfJs(9hpRoW0s^AzuB|;?z22b>kryB+qVcl z4%XJ@Om-&kR!N7)Q;zlv>y<^}OR70kDGZ8S7rI5MRxO%y=27Ptf^X}2 zYu=Fz98WJiqA8|Fc$#NL*tD~KEps@fWx}oZQWbD{_gbA5+JCf%UitDZ(1ACq|w?xpt8C1l5Ps%o09xUoqDn zCw3Z#OpkZTrkeF75LR9LrF)o#wKuf>Mvn;ftdeXBS~h4Ugs>u-Cb%P<(I{)_M5G(I71WAi(hb;nPE%87+5?g&>m_R}*VH6`Co=;_9sSksQTM9TyWzRVO6lkip3~XwzdK4css$!Lo49QiTU85% zWEd@1a_Ub|bneK#7$YY~{UHvSnH88E&WN~II{(zbe)&|xMsItZs#lpyTylwaK_Z9Q zzwV4RQ^P0ZjK=!rqs9&8JLdn&dM!O^NHf3vuB(G~^a5tz5wG*@&Cw%W)N!jyllm&f zDu>wk=HLszhSmv_I#G5c-vMG-atxj>_BL^$$~BR1vD_-ZlJB+Aj||_Go}dOmn@>`iiE{J+u*}vFrD}YTfIqstNH)vyWzJx%$U{D z7+PaIe!!FCXSqP7cj{W(yOMAE4M$=xtXkcf*YG;GKFKIAEg^mMM}*h23*k-S@<+N} z@wt_GR$d|s$%ktX&>HcL(g_aLT(37x2>yefN4z8*6W5p5 zyI#_F;#(3WeX9wizmeTTyT@G>szU?3aWN%NKTb?qV-xI4%@d>BS83)(PF^|wg7E>t z)K5B#!~0#i3F=Zk8J73Q&yYfbb`FZtt%;iYGuP{fuF+~n(4@c*@`r!?jawKPB|NJZ*bN7>4DxSR{QL$1hKw`)yHa{wccj zDYMs9oP-HS2?~p0s;#vh2c>Elh?~AXPnDwyLz-fPQhp67yn}P9G}wsn-Kf->hjN@%g>YM zTB6k?<$0S0R(a?(-mCOC0wRKMtp$FCKRBBRZFs%B_BW|cKj#{&6A?*q=8uu*JZhTu zq`Zf9O3<2`aX^bRL;iFBpXNaGpKo!Dr?yTFtnd#A6Fli^w}#wv0vF|vx~e1pv-|SLdb&lG=5UVw0mi`PcY3WkX$37_ zsFFwm8{kJXawHBk&yU!I-445K~%s78` zKd4d9$sq;va1u&wZXSt~FwnY4wP7X}V>9=?AdZCE6$Z}~J0v^^PTir z*3$j8YPpw&(Br}b*_vTp{QOtV-M9nEF`erw_E)W3WQpyn4jlAdCS#?XU#r7Hx!t7o zcp}X{>^y7iEYr>E8lWrhM|{gUcKnUhCy9G#){MC7Y3qR0-olTaAA{`*q}oO?Z}R&* z)AaZ8Sr75gyluDm;^}vF4?o@fj0k%4eut3=>f=`qbCaBNn2lf7kqju5!cJ-2Jh|_2 z#zsKP)XjAEy{H9hWBlflgQRV3FtN^97Ld4vkX_bltxK9Lk7e&mJy;J1fGAV%A6FbBr_Wa?0Hw;5L(56 z*^0-@YL;}CY0$zXB!A6hACY9vxw`|NF)@>EOh3nhD$v@a&g0LGmIyd3u09c{pV1~g|xfs7Uid>_C@=_G<{!7w?sNM!)NPW7NXz{ z3N=0Pi7n~I)G%{(6i~kPv88#7j(+{S2BsXAliT-mt?j)!YPMwOlk?_8opr9Z$WbMx&eUYy?5h}l#aKO#t zxsdRIL>M9NfrrvnOC%*^6AI_kBO)T6eptMT)5$s5YQ0m+@rJ3YPF$FV)5MFug)GS-jVR+#~w&h+PFKx4&!ufHb~=ABhT62j#9h7+n}Z{ z*H8Uq*Z%nWDG=P^7~&zl#E(%*lrNM(&AL|%ECWxp?f~DcZi(?$>d-9|Cu6AQAsG98 z;$-;X1p+f(K=#n7DCqzRoiyG4Td*sBe{Q68ys91)!fwSyly4m7Urox@G*f}!6$aN-U&cw>x$@9;C3}YZHZcaaJy0iwk+T#88cE&u|W~Ya-)~H{D z$d7ej)}A)~UcY5$ZGH0q!T@^0se;&r>0`JS93_%v2eq03R@TaVQ{&Ncpe)WgN097+ za>zG?J6n~7QwvgXX7Yag5>?jwsY*cS!Vi0Bt8Qu>&r@7#`9l4W`L&k#-x%2O2_YZm zjs%a}cV!Ikroo3kfg=qMwEbHTio<)@M=%94B10~R&|j)#isjlR57!LyO8LV}+K6yd zqF(o8gOlUi0t~U)g*z$IeW)km6{C+O9)49EWfLkhb(t_Un;6v@aiylSmyvTai8ua6FKA;i=ZGYm+uZ`(IT*liy&#FivL{j>oDN~*iRw%)cM82h^h zhg0img!%uMT3kC6;6_7!7F2B*eQNgs7(Mu-C%aWOeC)v!c>dhs>e-TkD)W-#Q@v9+ zkOjqw?!I`&2o<_73n6mW;ovE?g&nLzM_}*$Rxj6Z5UZBI5VeQRcfXI=r|%DI?Q0B1 zh5m6|X%0khWv?7_@-|{Zt~LzPCW}bpmhtg=4sMoZ~;8wn5etgo2g)-;I?T zZ>~)(ReiIC>{y@R(%qbh&b%5_q`cU5{Z?3ngLOy~4_)<=A)lyT{Q`6S7|+kc+WXme zAYvdqc&rj*VK0_URDLe>Y#fn-HtbfF*{J5wXLU*^2G(HRq6NvHWEcpRctLf=f6nCh0!SwqZDf97UTFPZRtrZJ@3n*U3W! zbhsKcl4;VLrU>Gl!z zLWDt3zfE{ly@p!5w_uQ;`|<*5S>B^5@}2y_y~v(N*tK8Ykk2644TmI$ft$@*?T-0s z{nb+nFt}UHP-gP}H7g3SGIfB>0<$5>r_2Z|Y*oVy{k?S4Dv_f8Julnh<+ieFSQzbH zVVk`71X-JA`oWw|kV$!V`FtP79g7JJ}jShYp!%gVjk_bFT5{9b+p@p$JT`}N$Ff$FZe71FXbn}!oBjtkuFtlU3J4pw;XeSH$nrW zrrX78T->|CJE}c;iVoXe$25<5%+|7OK*GgC(Cm=lKb9CRJ&;$C;at zChXzev1fB<@wP9h^~FgeSB*jzkS!tZp_&)z-_u;zUM-&hSo8m9y13 zJbOD5h`dMlg@5Izc8K@h7W_8qFH5I8D)%9{r|+wWX-Vl1qQAUg8k@uIG3t2M;4u+&5k-Jjixpi4Wn-NO{J8 z{<6j)2L@|kXkjE@|IL48T0<$8+qx}=>xXjntZSXDAAD&elJlU&v(`J>&$K^jNck7p z93lAf!h-8iXgNB355h(cI%_NBV^k>`{X&~Zr#N^`U=~HmU=4=OXK4qYzlVc+;j{gb#uIPlO0d|7DD$#8V2Vtmh!Nz5K*WOl*Ta>)*# zy`?gUgzOVKGDMd1kgzp_4~xbs2$mIXK7c}Y;}Xxeg+_~cM&uRB3`{2FJd$U74>)Cp z{)VxBN5!V=s~qJ{_$&wI)#;y7Kq&4UkK;Dl!2GfVzBe z@H_nZeTmTz{`~LZQ2H<8Y-9|3?nZWoS*+%f?$h~oQ`9kk1!R9}V{SctG+5y`xHV#e zZ42~3<|vF3t6K9jAe(L4SwF}kfr?&E(k_;a5bKr9-V3i)7%5PN`ini6Yu$N|cP8pw z2<|v5+%|W+yxRC!M`+P#X{l)m@HK1ya9?;FdOM;@QH z@y})U&++F33*^RMzc}UVT@$mcGwFw)Y~)O{Y~ws;%#BGlbeq{Zr@3bS zrE|#)$b9h>LEL-}%RXsRb&tjMW82b1AY_tKDvaHI=Bc?TGM(U&7Owy7Pv(DZ;=VQkpk~lcV^I|lUFGSaVr7FV+i-L&+nh<$aXJx%z zsKIk&^vA%#?WZbxVOua7`ffW%CE43&w$FS07G&|YZ4I0pMp&^32 z&VN*5^`-~{&MYP_>`54RSXi0@GD}NUAKu^yL&6{A@3>SRPX7#MfM=!=@+glbt0?vK9aLBeiC-j&oJ=60q$Gt{4H8YDHD8` zG^at6U#M8i>hQVxQ?35jL%s(g`@&HO3+fEH;uHnV8y@Zqv3DvJyVTwE;qwEy1vNL~ zjTKA9cLdK-MYbHf&_ccvcFULFW`3dqCP*r(RIW6;oGAb6Fx#`mqgUbau>v8>o_mqA z@!z%+*A4|_ajE!&Zuify1;EU|_tkIpUztH4<%~QA{O}z~ywCe4IY9!F&WEtO9@!zO_$VXMa4pYKOx2okFsQ{>gD-A8rp{q8)bTp`x}W9-|*z z`tS2@RhKw9^-j5*n=w@NFMR7)oU3>XAG}oNF@GP1V#hrg1Vh+^@jHOnNe?K@Rg{d( zE0^lA9J#0Fe8mx}6zZ)ftGIuPPD2R)FyFw3mjNF6{o?OV-S?%w@UKyE!J_a z!{5@ISs9$s?-hdwpd@@O1I!K#)g|uW-{ypz5Aa)D&tMJzYemJf>iZx|$~I{EYs#%L zyeNv0w_>AebAzYa%}SAP0TDztIl0o4xfzp=SMw_Oz_hIxOOt3OKWYv&Y5P26LETn= ztVOAHm!>%n|F*KrF)+bz4Ky}zOWq~`@1ShrZB$xsp6Hpog8FcON5(AG`4lsv6u(gZ zWwSsq!y_0RPK%P1ZEHc4uq%#A{#V|v`+Z;&Is3IY%Dtwm_MEYyo<{D9F$~GCplGb} zzq$LUbzsM~*KMc$TMSiL8)UIDv(G*~A`yrB{Wh`ELzDBYvx|B*(~s?qlRb z4r-CyM#!rf2^_5>_c4WLRxr*A)5=wz@1er&M$Mji@mL2*V9`sd^!+CfJv#wpF*aoX z>7Kfhq_T9Ov?r2pU|Z?RjRed33)7|A_GARYe4vzD_lL;CPNFXB%Ak;A6LRyvlpdoW zeD~kyquFh02{zlpefqG|Gj%idV;(S(y5wIYV@?KmpG1Kuik| z%Flfd-`tZ->k#K?zu;#n+j>US)mk4T?Yk3}FY3zmA83gFkE`Z?#0}znh@+PxsKer+ zK*Gvgc}}tT4>G&vwkDfnxBKlnSY?2I&AX$(afc>B+wDFg7wqQ;?0lCwDvt2MZ!J6* zHjsQ4ah8S%r1CjLhV{;{Xw{3AM@_hY;U9&Mci(kV2T+I`nq zN*|iF!>_ZK;TZj3*0~3j@)2u08nzh5`aqupc~x5B^8Z88|AAlIcl_^H9iLjjo#z*u zeS86}a~kduWnXFVjK9yALx&H5hV?6Z+ZR6^RrrtlqnVBJIw{vU;iccUka?1YwS4-h zOMvP52za{yFSE0eq*B*4kmL{1oiVf6)Tcyq-UYN;ZI)zh>hH5KCiPujFOOfuBVQl3 zXVyD4X}I+xR&d##M)GVa+VO#s5%SJ-heX};Zf@0WUb?QF-+RP#4ez;RR?^u%X}X!B zuYhdX40gVRQmWFrU&9;GzJ=kh1{iz9YRP#3s%g@uQM zH_rO8D>DoiD6y;!)H&Ds+8456$|GouddK)m$|0XGRW1m*Q3vTa=&kQWmEP1?)Ym!3 zwt1+8I#=3pC>}DRy&;1(O{f4F_Yr7XpNh`KFUj)_;030sDOp0I zWnx*0El;yjYYa8;P|MT%f*LJPcuFlzS=s~?L{jGw@Ra8R=#Tvi*XO?P&+|Oj_qlF$ z%Cx8~ZeiZ;isrcU1^qK@yhk3`vB68?HUHja5aVJi;s1KLG4F|RQb&1sW7>{d(^lD{ zQE8x(qv2{J*853{=;S*)V*`c_l46VYlOmN%YZ zPB%-}rvybxYEA8Nlgr*4HK+3}x@*?{3H0l6{q~dAw+=?@oj2X}5>&~99Pg@EsjHIo>5pC~>YugJR%V2y z|H*);8>KcB#rRQ5+%^VuH7N?4kt55NwZld_{4H;+#SejO!+?dRCn;O~4zq@zBLkR^ zjH3J%vo_~$>$TmXQ|$LrNvP<%q9{FCW_Yck)g4>(S~YovF7Xjm2S9-->&vjM+dsr& zgge0R_Ef~nyXVE(6W8+$HU5lGnD3R3BS5q}&_fGHM-z7*`YP$&tZmL%D?D%J*XeyZ z&U?X?Dkr_MD`M|MMX!2v#6#=~D2g~VXXXM2ZpFLZh0Vo;R#EJetMM^rw&5SC5Wn?+ z9MCXh6>XP<1;5<9f0bFpi(yS|cx1Z#0;yg9i4;f0V2;*qRFa*jA-Df*{pq|68UAXa zG3u&sKoQLGE)2+hKw{i;f6tbq&&FgvlDUwo3+gM@K;_@WXF*LZ5~K{|)GSkc~!Ac@?1? z%5=|Z_`&gA#0A>fhyaJ8?ZUs_h4qJ1%9?wFZ5`zo=d8=u_}jmTGirQa7%&>8zbLlM z03ufQVJG(dlLFVw9c4sU<263z$vAppXcO3o zR@@7H6%KTz%L4lIKLQoO5I z3O0{>Q|>u#>E*ox7!!OT@M&)p>whv z1RZF-nio4k%7}dhM;x6xIVR-x4kzs66A`-H*wL{9GE>OKRn}oV0yq}=it3qLW$iWY z9CBQu^h9}C*o>spi#}8cb6V3LUUO4LHBB0p(%nlDf#6Ux5rXT}MxMjw{mV z9#Q`XvILs^36$f&G|=NZqIjpzB^q;BjK>MX-PY3t2dY z1k!krGrM$OQp`i+qM-U_$`w&co+lhlsETijAtoNtqz%3I`EJi(;a9#{C~_i(aeGU*91dpF=?O8sJzIl#V;|=_ zS11%+;#pS5^h3%J;gq_88p5-hgllo4!1Z~MzcsnF0w1 zuE4KcSJ_^S4QLfsJ4I)|>!<p~LH}4R zehJGkbF}#TtjI>s>cvvEkohqy1H1)hDl%Abz51AX6{}3l4DU z7}+aR=MIy=OtXy$0o|M2%bVS;37E2j>wWUQiz@tNNaylLGhdXLI|6Y>@y71)=Srjp zNXe}f`YW@Y6&si#%^$4xD|E`f@6M@z_r-};LCZ3@!~RJFq)L+4yl&D1WcBRcPryCRR!>tsW-FoY zkU#m^4^)6%Dh~$>BTeaJyjOksO`*3o7w1?UySk;M`56cO*Bt!W@3+lF-RE>jUNccS7Z2TKp=jG*w4D4&;IHX zYk%1B_%B_7F*fh^fR7-5^KTR-Jjv*Fpg9$5iel0RC8$k~`R%Fq7ChY?HTX)a8XjqJ z+-9K;N#IAtu@`;F>x5ol^kKmh=L)rJ%i>vu4$E}q0g2bInjO!qF5GDZD!VSNfUu%X zJgZ1+fXxTvElyO_bLYrmE?CBgm+KI$izXE)rp49I$R@gI?Iiy8WXI!P)R=9e0?8%6 zcL`Ec(LnBbzU9@AH^TjyL90~K?SC!r0lM?Tv6o%Hg>?0=0Jx@3AB$a)aq2B0hf4CH zobDsfw$AMtH7nl{R|T%zD>XkBE}?42(9a*F4kYS(f(1Fs~F`_u!YY-v5sdC(b`a|F#8KhPLDQ56;$ylrl`xmS?$V1rQi zFYzo!pXC3)eJqDPo;#TVVN7_)$-yUV)pP`wr~fXjJ+9}3(r&vcs@HYL7X;dSH&(&V zDv|2lG@YKt4Lmj1Tm5Ehrpa26AJr zy0-)i@c)wz)@Q6%((g}0ubYAzYh_)V)t%hL9bbQTaNFHdm`P4m5NC*7t~*VyLi*T7YsQ_{Wopo zzqvbx{nS*873FbV9iWZiwO*kANC}UEV|2pP>oYSd>~5^ETgnaT7Hxy(qCgpjin=Ml zi(DY}EJ0W^O2+!CrSBlh9JP1t&%T<62D-s*k(CVQ0g$`sOT9 zT8F5EV=xTD1i{PLPw)5-+$eObW+f#y(eeE(X5l>NdvB2eFpH*g>`Pv=GoJXiiWj{^ zFSvRS8XoZmUWBXFB(zdfUGNwibq`#)3C8{m;&sr|-GBkgNV*Qq{!Dev&oE+_F};oW zKJ>Z5Je;h$7FNAW66K_$=UNMOTZ=S3wXg5eT5`zM$B|gj0I60odOH9`0qlIN{E_&& zft4eeDV^E>Mwn7O^%l>NSeFQAus>1`Z8WW5TX+y zm@?v?eM;q5-q{qxLMaVuubJSsnZG9_?l#YZ&bwyN1ntfaR^?WMGyrnmU8tiXB#yP< z86Qm7IPfDZg)K(boDXASjRnJB4*9TPcQ|QWM@wD67B%sxgB3WI%m+mD^;u!&BO z<}><{T4zA*3l@i|`cLw37ToUIpS=4$zpW9DU0zIMXS#m+lnQ zQke61WDzk}9D7`zIA6-9q~qoU$~%M#4fY5AvV zj9Xk*2e#FMMZV*GY0c+I_;2Tyw)}Vf+AH7K(p7bHGw-L;am}^&3*?U|&(iNN-v2*+ Cf4K|* literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/topBarLeft.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/topBarLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..206b6b0c68e44040a36b63612c2c79f6378a017a GIT binary patch literal 16251 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 Un?kG~?f?J)07*qoM6N<$f^AbIi~s-t literal 0 HcmV?d00001 diff --git a/Templates/BaseGame/game/tools/editorClasses/gui/images/start/import_d.png b/Templates/BaseGame/game/tools/editorClasses/gui/images/start/import_d.png new file mode 100644 index 0000000000000000000000000000000000000000..4b30cd1b51360e3581be24b52b731af5889ebffe GIT binary patch literal 52356 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ືV^_ryA?w@lV+ zj>a>(%bQk?3308%l7-c*aZ)Z_htcdH72 zFZZwT`#(lJR6HQVk+0~#qucOp=DWoA84&~XTAYecBYzdB0`dJ}VVLaR-s|z5HPap{ zuRfE$p}V5{iuxBWX*lm>*ccrhO$wI0f;Ilib$Y$)_MK`;?Xk5dhyV&$BzK&NM)}$; zj>lEsy5E)`fNIff{~HcyUoW^*s@R9*y9iOE)TWhY_c+j$v;w#mARR$1l{iIsK$Y6o zo0WTZ3NB5r{zUl`pjAD_zmX1^l^+VYYQ5pAJ*NtMiAQ6X0=^^RaoD{u zj~_`t(7mAlPVpmX5iMm`EU6;h)~!*Q56NJi zxpmI+4>A(-Y9Je9W(%nbVl_$lYtGKj1=m@@P98V0c5m9m(0bf^0&j= zG16{e?chqUNJf7{cT4Bm<~VrLsR#7C-!nS+X(hp^{D%X`5$URj-(Hw2U1yRTDu+{; z4I3+bj&e&FDhhocWF3rikoz3~Jmla;^Ra(r_hjtt4okar!h>-MLo?U48c{26-AcyZ z2obe-Th<5jcwE*bR&Y>O{|Ekk$5PF&BJW5G8&499#E;PN2#!3uhiUJ38Xq3tnK$Eu z45siQh;#)tr7lrR=C5f;TQ?saT-lI(1)D^@CZ3P_zANO(OS_4e(Ge06&Qw?0fh1?Q z7nzGp%3MD67nv8%b>_Notex4NwCj_v7_&zAsmVddrVq6fj%IMHrARv$>|)WFk;2HyYhlsZtf`Zmy>5)qAhr^)_80IRY!e@syZFeRez&=_am6;B0xw zUPNp=ICPA8f+OVx0$c%S_|{ebnPC;}(%=Bir2oB(Bsie37h{9{}}M!d7QmYvHueggtQ9rbai{! zb({yY&%o~Uu0!G<0K&b3YCuwhXaxJ(RsU)h^9KlW@FRw$6%WE!3&gC`hynMb>fGm? z@&Z~a09&Ym*04E3gH~7x*TxwBV?-2iU{k1V8GxpS1zIh)cHZRU@Guk}4=33%lF7(q zU}6$~?GBaliyYhmfSAOAFIagr$WmAb=h3t%QyB0y(Dny48x;2I-g#T}RA&GrLse$4 zla2%K5Y^Dep>wOsEp!7ZPE>2S7~t%bxfNS=@tnWOcgAWAl7Mdp+xmJX~0OfI}+LZGO@E$cyD-dE5D#Sf!4GQBm`k*dJrFk`xqY|aZa_P5$50u z_%U77V%IUsu&@=^&_-d6?OOF#ZA}LGxMO*SgF`!Hqq@Lay*XZo#&;v6b=#R=AuACm zlTJ6rM35rtC6zz!X- z_H!SD4kt#BW~*|_6j-T53fQlZ!z#qFI@p~Wnh>N2vh;76=|K zX^yDC0)=M+kpMKn%FsZytVLU6+bRSEr~s=eC+7?f4Y&zW@=giKF0khld#cw-uX-cT z1D-1HX_|b;Y6~dnJY0=a*qspIMx=y-D1ar<#PhipUa=QBb{n>1eKlYOvk`VS5bgMr zyiq;$bTD+RBul{lv~-QJ1QkP`hAqlChC)R}Y=lNsgGgp#YylbCj|4D2gL}gmn0YYf zelWw&J$J5d*vxif8BrC4%ZZ=b=)x}<;0Vb!L``j>8Zo*Wq-*27u4y+8;%C=}947Ge8_5mi8F|5@oaO(8ZaRe)2BPSsF1b}RS4X_4e zXo(^zp*cJ?!})bT_6yNbrp&kWLaS6ivjJHkCO0MSd1ek!lVQcfl?V?i)}kaWX>GUg zoh0@s#2iYqdM>iEj3wp5RBw*^CLiDDh14nFHmP@0Vw4%lUK#%&j z08svA_U2^IfHL!k8MxR{xuNr*4630m(LmeupQ+=g05TXFX#vEL)s4^s58_JL3WjEl zx)BQa5p6<86eSg8k=dFO0ltMbYB#bu(3l6x2nbQv+sezM5x*g=b&j$v*!B<62z%9<`39I2^FuGDT zP(DZvSZ&}8^s;-HP`;u&gW5Fkp|@Pm$Vw~_Z82p)D{W|xvPY^(z6!lxNenA&*f8A~ z9>rR0gc@pL0j;Su7~mZJ2>vxsP79n$nK z+4<%8TEHD<0W zZ!8838FwqEbBHnNcmj;UP3GSr7BC5q;#L?Tn+kQizS~sSVv=x>nFT=y)9RoG-4P#x zR|igmrm}p3J!8{DG)E|iDar!dMlKMA!tzpvJVVV(SCa!ddf74UH$wjfE2-NOpIU~;XEP8 zq9j&Qlyt%)+=B-H7tvZHMeBD?aLm+AsA+519G2rsnur(+#B?u2-S$HEpr(eTu75>ftEVWZYN^`LP1v%OJN)7;{pR7wm0~XI=ZUSjFuYyCmQu^&E4b4f?5PD>SRgjA zJB2N!gv%qPdlVLiM__Jx5I1UTSSy#oh#mmV!IgAlxHDIt6Xy=6#HmBU%wD&76#^0R zB@Dohzpf-sLBSjxffwZaA}vSx0sd`b9u*B@jTtj3UGMXOBa$m62b^Me6an^E@5sFG z(3jYqHM%4|VmtU3>ir23zNE1pq6%sj~G7+oSPk@Bxv(`yrRr&5ya zcNnGXYXC<>@;CT-z)hiVH&gEelj5F*>(vGxphn;+aTy)DgaWQavxZ-SAwa)PvHWZS z1cZCo0%&CUtk%ND@BrNzAEbr2QM-XGV@eMzfRa|ija)IIn*gUy=Z@#V*r9Nc7c7Zm zgsi~MEJMG4`=&2xp*)L{Hi8v!OEHQKMjueTM?5adYQ=%c)PND`7EKT-9a<{Hh3lw^ z?_bz00~vJQy8+WXL~dQRz7KJ@VUXY^AaKHQiKZ1Tod8#&4wyVdXLd=O7#KPe*2p+z z3A_d!^xWW(jT)@N;L~98-9?~dEctbs`iNL&Z`rC>lX=oU7E z08@&N`6#Y{rD27XYiKEJ5Fk2OjM+b;N|qBAF<$Vg!znQ(s^}@;dM3#PH3LmhKY`tC zXFs8|Vci|sW72So*1^X>y{{bHy98BTBi$M9xsjdH3&?i>raQ;%{^?AU_aSQ^(00!D z;ht#^0qou6#R*BA5~Jkx7WE7)Xi&~#3Tg{C?i?$!ozoCKMX>I!bH_%uJAUp=g@eg* z@A;qAa(%R~|{-Dt$^R(?V_q7n$s3`^nEX%o1DR$`sAR^1A<6wn4leFz9QrW?~w z$+HYYXAV<>F<=>CAuXJO6m~Be7%-B9RmeaV8IQqzA#=y+ZHEc@AbMh(0)0X2E=IdY`jJq5$zN)I~V;jcI(EBkO(!BHi73cJZdB1EMjun^Y3 zRIs(&s~*YsJ@zeiY}9ZJ9p36WyTKOpRq_W8-va=bJ9C*sGSH#DK&M13fJVtT$I+Jv zGZp`L&}-w|@F;A6T1`N$=yXLlMKNI&A?bve0#m0E;EEjJE|E4Vbh;_-`jZX-n?Pj0 z1oEwkRG2qqG9fqvh%l{S7JL?nVzNWZsv-$=`t> zpZSiS`xt;%$3CU!?541bllE}N9-&D)=|tR|U9_X^n)|K2QO2l?hkl%zZ9yv3lD+IRg;_pplIGgLAH zF^z+*0UaLH;Z|mB(!pE@;C)T@o%9sZ@3Vjhe-G%+5RLWt<0zxE^TeG3Gn~-_)(GY( zp#Vp09;{l3lA$8U)QSyigQj#9%!7g-Q6utLLFGeOc0vcL03g1e;p=zM$+_nz+h9Sy0RDJAj zbsA!PwAH3TZ7`3bWI3gO&1y=V zI>cPBW0!==QMMj?v$%-^uLo&&DRv3V?otgvFxv}uK$Zzz=2Qy80j(si!e)-!d3`<8 z?OZoA&F-~Y>wo$mN7XNiymTCNT5{50nS*+*!Kt8HoDw$R8G0V3+d*^`xaauPO+!17 z6UkzBu~#?x#BwlykD}>4;+ucnMd&+3DwCu1MHDKM@Ib|8i&DvVoKe)bk+$MU&ej5QIl*VQhd_SOY6znc^sgMr(rsEa^)4mu@`e zvrbQflsE4Lk1!Nm1B$=s=!uw(Bpl3 zGt=d*Zs(fa+5JMxB~}}4HmF;fp>FW;(*80=5E(*iYv>38Dwk7h=Ms>zvNPWL^{c$xPq{BanlCcz}8X?)C@ofxCiFOI_HL6 z$3(c2aVxc;3b+DpME}yA#>}o$VoXdJ;{-D?mOBRy-5DFIF*Bv&j*7e2Kxa0JoqjvL z+Jz18kwUyJwh*)u9^2Vnb9Jk$Tf4f|{X)y77AtJF+HBPZIKq24vZ_#3G{b76i);NV zA93`f0?=Yg0E%7}>%?ZM=x<9s2FYlv7Z~=WF^yPRS)M?nM`1um3iFA~NG7S+(J4kU zf1W;PNbHe02n^!=CsdQMSVHKWps3M#>135!a`Pq0z6v43K(exTt#}8r*0fc+X*!W#|{OdD32Uk16nT= z>WqH}CZ@f*Jx#ciA$8a-bLE5AI&~)mIWf90sD=S6+s?zyovvqgd99n79u`_{v|4Mm zL7k24yr(ZP9vz@T0DvPtURmdbz#*4v(wL5PBice-#t3rjM75Jfz8`zW7Wu~r29Z>& zBbQkw+i?XM6Z-*>4Q?^{Ol1L2pZcW7+db=?29eAT;vvb5a}R1Vx-oE%bHaosVaOO0 zzFomD1xXU?N7#Qs@j82ngmm)(5Y?X>U?n^V55m$oQ)pm@7~Dn$?3DB{(oa~9;Z7V* zsR?mHmVk3vKRr-q4QZ>w%u_H?A=GY%@3_l}t5qTZceJ}Ic1_iX@+#VO(o4KxT zb#ZO8J1tgPEF(ff-3o&D@CChxpB#sz9jJ1sN?y+tLuI8t``-lzRw!x)pjsh z*B&xDq?D0S&pSp$mCj6_ain31^-6f&cby^=vE6QZdZ%nMo4^{$2Z4jtOim>jG?ID- zQHbZ{6<)F`h(?IAWrw0dZSW7G6g3&B1B=@<(AeNg*h*VqX1q77fQ^Eo1)^mO3ZP;! zW^n>c9H!KioG|B=Oy^D=fOb0`7qhn?%I9~NfgQkB#|iXoNI%n#uOgvL+RU_6d*41R z>#G|?(suWt#Y(HSR%_Ia+7`|wFp8w@laO>+oWt$hFgLs}dZ{0B$YnT*g*K)Nh}YfS>y;GOVbx)xSaP-AEvL>6PZ5vvn2CWf6l zO~HxFPSxrlK4T*$?Xm`I*ulf#DD7q%>yE54pPGK{=^{Ft3lBJY`Df+yrXtcZbng;$U@EL`3@ z<<;2P*4#p(ixFD0JTSBF-CF`cpTDP;)e)V&TS31Y@$`4`tl5FlfWa#A=NLq~5WwbN zQO_jM6JvyX6_&d!z!I1nf7{}hg&JUPyf!`vD?P5!I=B+ml>Viwrfw9qwxgVAm@Xg9 zG*I>dLL=SI2nFaF!@=4OEr+plP80I!nu7YnOaP4LZ-I^B4)eSAdamnR-OLkddt73> z)oP=*1<;eWQ~zTS(&v=6)pr+q`?qnd+2TngMYNbwfUAHlu!;6mo45k%=+=?Hh=SERhojItafAV%f9pa5+V=4kH|Vz@AE(p(?Yhen^}8#Ygf0rnd@<(#WJ$n*lZP3GJP5$>0tNJ zAA;dwJ=pOlpMQT9{$5_}fh1p0rD&pCQ7dsNN-4ephHH=+odMR+;1Zij+Q2lbXCr_l z$I5p`b=0Asm7sVRbzqd{MpIV<$`}F)sF8d~3}`6DAOh?h0R+HOh*$&ua})kcs9RN& zC{jphQKy7++Y)|EKVfz1swe^&QH~k%BNcNIzhny3Pbz{t^QSytfnD?ub&EWtW&K5{ zV-g6k06hh1!>w=^zQ1lRuI=NMF0QQ!2rU*XMP56SwhkdpbL{bcwL2gfKH)A{>{S?Y zI6Q!%4w-6D0jNq`TniE%p{&sExZ~3Kf}v7Sye%2R6249F&`T*gn$OI24U#*eGrf(l z(_MV~xp{Qf>Tzo#*BPaj@lZ>E5iQh80ji0XwiRlshD@-;Zy6*pNUhicL8`$3&BdkA zNDDP{BUqKRicqq~V;|a@Y`0b$Me~w|ZD;vrVZP+7@k)8GHfnGiiGYsbF{ShwDE? zSHss20cco%4XD7ukJc7i45XKSc+>&;09QTjkE?= zVx#p|bpQlE0xMQ8oIjz?T=AAVD>&{F0J#XDd5x^|m+b^X1L0!i2@<2nOs6q`sD3XJ zS?{Nv*rX)ac0jFQoa_3gxwyjB%r35VGq>d$O{2C&8_>4B7%66(sVeS+3wHr!2opmr zH7tDMr=#$SxWU<3hkggih2$}A@CftdMp%hwg0^&Lut72hqF0tDHoe!c zBr#UzHwU-2%!fx(2ndK_6^x-#yM@~_G%*#Yl2tumU?DA~2e1`Z;(an_V@x3)(6nfT zU@6C+A94K3P0op_(>WQ%k`6!#D4S`=ksm}&PSmR7zI$qx6>%NU%|R;7EJDkusAtGo zh@Jwp#YXk4y|`*FZ|u8EyPfOqLCdv5NMID@+Ro8A`GUUVwV&E!9X!DkP&EX{y?2i` za(f@7|*+3MoT&VE%O+ji|}#7~H8^okt=td=VngLy{my_4IPDeOe1cbS&Nh3@Aw z6f}9*BSMX)L7jf>Q&o#t8<&XUY^)6pv65}g+Qi+$DHzQqp2VMXdd_68eM+2CBY=gH zcsnTFPTRbA%@Byb#CS%(7PU~&qb;t(#r5X9E4#e5>$#RI zbfm45Ydc5pQI)L9;a>d^obgPZPr}V-Hu%iD`KOB;{dG^Ki2g&ZNwikV_LUGU#ux60 z-F~B;7P)jRdAuJVYHLYN94vqw6(d@IJ8-rt4 zol+-`C(xN{9dbdN7vAXOxg;%r!^#hj>Qqaz{g9Aq0jT2h>_EB^75%3rXgM?) z8{89yG=3mW1~S(XziU`8cI35{THziFP^-wG*MK>opgdgE<@qGq_S1r=z`4Vz(*&Fl zt{{9Orb!)}K1V=j2TmG}I@2iiSAX;tM7gRu%de*0fQIx`xaUM>fHc6;!bW%7s*eMFXqFg0cb4 z+__Ec-AwXrFx{p)1BblR^!&eCc{C+Mg-|h!5lXl*P&tj^#`I&lbK=w`6M)je6;TLD zm_%BPQpW*z-N6((h7W^VMJD-5qv|KoiHW0vDM=X;Y_S9#)gm;w$K7pvJ+qIOy1231 zxgHkS)(V+JiejWp+7KgW{(Q>St?FBhrag4@XIv#l4c7{e*a{0_ET{qfpfWn5(LL$U zIyw>A4t3YyyKe)+m>AKfzzN+8SH9rr1~%WJ#3)y0j??zGydZqVJ@5Q7&Ka0SOl zG7|rs3bEPhX0C63k+0U>A0~FPhm|bpmzt^FKoG;!u z|Js$WUG<9o72T;r6efA1%=a;b3@`Bt+F`yt5A{Roj2)3*QS<{&;LgFghVeWAI-vxN zYOu8RZTSA$zE2#I%^$GcDsxB!(pLN&5z;418}RYczWqB72U!Ly03g)j)X9aygSD_0 zMc5WTU4w=Sx(;;#K<`_ycV8ObYtyTqe^v0TP;^czDP{v`803;kUw0SN(9SKJP7eFd z`sv(J2E-1`(oo1`X6TYX%$?>p?Zx%>yDPiA*3I0K&Oh|h$fzMnVx%vS^h^;Pll(nv zRDg^nBw@_LK_hA-E(Dph#PIwBhkRA{Ei1N%ynbYCx(k@R;8JR|&hkb;wvpM}y`g#4 zfFDY)4e(feeHaH@WD%2NKwE4~_qw=lF0SpnD_vgO-J@3P9fSnHdpg27j6cb1AFRQV zCx574KUx1M`}@hS!-qd~I=jUYRf&(6K~2iKI)ImGsw9o*N;Lu1HYPV*K@HN)G1czP z;Cpq22H>?bJ|&q^LS{blI6H`k$V`(%zNck*fB^QUl}IlUJ-aPtCeal@AtzdmN3G_~ z<@NTvOI_aB<*hv|wA~_d**$3^y+`Fyb&#|J$-C>jbGml7bFUl~{5-Y&M_)O*sk^-H z&Ug-+8nTbiK-f`u`Xt~UOu;$M?!|lP{lM@fqYOG1b0dTTFtLG3K!NZ~u8)IhbXWFl zCQGGgaEWqe`Zv6O`D|U2^|HWINY;C*zHEu@6f?pPmT6you_^sHo}9}x`_PTB&-8`eY3{W-1g67k*q!@Pm>YE6w)l9Z5C2Iq zUO0h)4+0dUNXD$nn`r;ONs_6~@-*%^`xld0SJV_`l_4^4M6<$3G<`Hg*;~W>IWyZy zS65~~@dgWfybc$)+mDxaabvSPJS?={Dnt!QlX&eTr0todKT1jtH~5KlJpSYi-F z7jll{A7!uovG=S%)-ZO-OMz9ux*W@uB}5z zzMwDAxBI31X%z2qGZU7;S-RD!^KFFa3QSt8SO*kKu{QaZQLzIc;oM$wG`JdeTxTSI zhe%ceJXr{)M|-@fuQzyj)cnC7AGKVg4N!oPNgIE@8j?TLGP}3OYn`1&H}MD^Q3gc7 zW8A<8I&fpC3r-+x$x|t0T-8Y)de$GH4P>Ou5A&16c=`YH|JZIeYH9@Q`1Itj?j5TV zg~N|0ZC}d2YuRjZabstH<%ZsDJu7=cCxnt%0W0Yov>HqX<9oLYBYoi zm3Q#`$USQSaBvWOfl-B#XB88leknhd5)HFtHsG`qTtPyGqJXQ2TG|+;D17fAW>*f= z3q?AMg8^jiU+EpcK+;dN#E>CZw}yHTyJXDb97ZfE)&iG6E7&p{_r}t(=E|fHLlnl$ zs^>cW*Dql8Q!b92-6_nIKS2_716dyBI3=7K0V~5s(iTdImv#wVdMhh~v$VVy=td@< zITM?{0M<{uELMtU4(WU6PzM0Sm@&kG*3cRhsG2Bp-98XU_8?3MOoRq9(oTMg3~FBh z>nC1J_;?jkEUF|O9R!~kJ4d(*+=8_sNNOVIokGYYXNk^Ip_9e;AyxXsC%pjm3y1Yn zFOK;BN`Pg1aCW}CK){5~ffdjS+lZIeKp(hSo$e+gI(58%u9dEVyX@8%Quxy@(J}h! zR%+4NCa7bw1$oiaRcr#V5G2}439CiYw;yW{OwgvRy%9Q_hA1YLdW`k^>$d(K%jc+O z|2Vz>=@nn#>ehIV&RQk^jqFmP6Vjtaw4VV&48>OL%CL3>3luxY`s8RHX`}Lo9%nwI!WQJXux8cJ> zC>*GicI36YBDVhBVViq(uv_PFRL}H_gMJ4ne5VX&wD*|@Q$9MXXQr@M;c&T*NUe8g zbhLdOyuyR}hr1xuEiP~Mw$~{4QBfsv!h&c@B8GuAz&3DaNIu5-4?7KZbhR(woGb<> zs-X?qE!s7jwdyrCcHk#9oR7<}b+{HsPi-%oJ=lgJBH`1m4N>pjs^OsiQB=JLUeC0^ zgIqc;V^s@4fOA>G_<&KZD7hDTfn03W$3LKnE>61#r1UkNfYg6JcZL zVAzGIsvIj8V`?o#LDlvV?n3@8k_mdvu#sAzfo-Hmw4fOpvNkP*d+|<~8SbRH^r&{N zW1gL#T;(6}a5(>?IAFNtPp#Kc$bNncj{<}a-g*WzJ=Mh_hW^wZ?|eNvfMi%|m@2x0R*Mr+tMWjBfsL73P=4~Bci46$68mI_<6 z0d+u2=!CB1AJ=90sbxJ{=dju*(ZCNH!{Ngp+P)6{{)k3?pSM1*W#=%v*Ltq;S?ni4 zaSqeqcmmw10jjz9E5HF}*xV(rd`WBL12i{0ifdsDHK>6AG-P{5od7|uVbKU1i1?ZN zqW@gVW~=Y6?AO02;D%wJGAlZEpcP~C`+ySqwL|4N54sl~gpIgPp{GQ!q^7X!F-XX@ ziSc(5w!lhQ93_Qc%1^8aX88U}za}7bexea`iGdXgqBv(gSNYq(Yr`qEkpco43TZ+( zvW#(7V4!Qo2CTta*cjK)679;u23E_}Uoh6sqd3QpS4NoVVr=Ui1sOCzGy_L0-nh`h z9D~7)Ku;DZ=>*VZ25e=m)X*A*mD(-ZAQ=k5S1c>%0pEi9g0X%ECAtJ(%?veJbu1Qp z_41T{L~Dc*WAZ;2K%)hqk<~&gYor=#5KuZ38v*-8yuP(k<+F{R!RXT&-2+NmT+2FNP3XJ}5a zEwPqDi!!hXUjXapQWh&+&B9mjJ5N#|#Ss!IOrcuHtN9$7?sC2%RpL$6i_x~})Zt23tNQ4E99h|WN9%LKwLxKO^ z^F^WJCEP?jAt!JyhB}iO6v0TMsE|@13(PLBUjXapQoP5vN*{eDh`H+ROxAX$>Kau5_t zOn`zape=A^a3`6rp;KC=uxt>iIZ zMMJD)son)0CNZV}7HkZ>lfnW58RHrg$(ocYR%5(S=Bxr;Bu6k>O88yC+Or-xstY?T z&uc?Jxt-ko^)prAKLRX{T1_48_Xim}JoWVlDLp@5_x#E3o?gSc(Z?(6bK>OavH&2{ z)DbyL_*!fYY9W`CPsFeyIrwbK--O-JqfTlO9?^Vs$91TkqJHo1VI4gIj$IiVs0^(D z_Ew?m@tM~2#Hu}|20lC(hoe0l=jW8CU{t#gbx{A%0^)E+hl@XaezXkd#e?Auy7+i) zI}K6U3f?${0K~l2Yay6yn_EkKwEYt+raH2Lt<9-g_QM|Sv- zgH`MPYWF7&9u8kT+Qn0baz_oShFdZ$&m5k;55rFP#*c$)dtWsOP_WnLCy&jc-PqB2 z4G-tA{@#*y)v&Wx-tqfuLzgTqqoBk+Rjw%DJd1)_L63qKsoG9zLUgGudzK1>YHlyl z7#=2$UYqKQ{#UMwhliZJ!!iof{_Vk zsGAgk&JoiIy?=Hjhu|{o_pryKHeOGe6COP9S3N^D?WF`M3cEm8V%%^AR0t0!?+q3tBMTw`U3}4<)&&72!!_C< z1HKR2J3;WD-jz=-%`0AK;MsIOq)%y4N%0fsMTGlP;yn?=&581LB;;}ORrj4Hl3 zbuZ8O?lr%8&#&L`^-G6Wjuqr4kzP<+2=I7$&Nh6izkiR?23+3guh==;Wa=wG0plc$ zl7m)aBWNp}U;u$#_KM8Nd9?qG*3d}*650Sd2K#xdbQ}>eN*i`a+jPwHQ;sX1o$}2K zet7G?eb4u=-0555g!B$>B7h38tpK#^%+5M;g9*QS<4k{wkmWrtZ!M;?877KMX+=iP z5uso)CKke#;q-yUOAVk(JJ&-eTdIBliGOaJJGla33-5nU7|X>_;_7o@M5I!c7>{^< z;-(XvpYZKVe)X1Le< z4pqtPnW+`4l(HRJ_z8do;fWyUS+^S93HBhhgGB0Nvb$ko-nZW|2)6;>nRBp4-jiP! zKV`s5yf%6;>PcIL$qV=5glDJt@S5MecOTyH?W+Q>h!S!W(9wd_5T?)oH6X$zewPqj z&N>t~CUcb%{P^vw_H@d&{gaE3=!mOZiZYb$001BWNkl62?_(Vu^ssw`jkdyX!)7Z-kmLu4*MDR$oqyxfleSiMgEs7tw#k_1 zr+oL4Z(g}?-|_uxcNQ(zoQE3;F$vZd0^1E>O6eZcVU1AL32w4jpeG~wyClAU%~$8V zo@qS#<1fP}%H5-`XL|Rpt;m>9h&#{$o_G`jgq9eihpJf9NJ3kHxD4&B92unI zE5{Fowoma`djgg2$>H`BdRj8M>rYt#W~-!#V6XsN0)pJG#IJWm$$7T>C{SH|{4{N)R3ZR~wmjwQ7yR9Y zO~*g#VtBZihimvl)@pcFY=gePvUh(QA16AP=NdRob0|u1BP@faHC%zrz;|{LQUFQ` zT&oQz2tVcamjwb?9p$l(is?E?9l`{5;p3?I>abZId;Mq^yTF$alG5eBZeW(gNaKpH zUf{)vdvS)Z-@32g@xvSU>YRAxPy(F55wetlC~TqVg6X17k_f`ZRKe;7k*-b5v@Zu7 z2Bv^G8^q;zTnIUz^T2H0obyJ;L1nh%GFC!ptW?S`wM zV~y+BTL3f^NcN-7}84#h}9+oG=3hIEmcj(L8H>6kCh_~Ett z<~={W<%d@eugJ>DRiuYZYN%Er2&-&;mY{`9y4D0Rc|bXPb%w1jYhL=2*>Vz2qN5xE zC_si5TsKE5S2Be3Y7i@F-UjHLPaX3S=ORY?qQ45G3bdF0lx}CrRN`hwN}l zhs$wv;Ra=RYl{NOjB_wo#4f(`|y_EzH{$i^W|&Dmqg*@#)-dD4HK+}HXtB} z#0;e|^+z!YYx226Or(~WZUN}CRWoNOf}BfmCxd{kY1M8QxSrX?tuAkE_MqFj?jH59 zP#fS2R3*K~ctmIbRfj8lxR`stKE+N9|2~ELKh@X$qdm^-^t{MspnZp=*SOINBpJn| zYsvo;L+8{3Kxm;x3Zg+J>NeeU=#xjqAMRH8)G7>0(vWB;XIK;?y?)^)W4t=!n^%1I zlHa^{?_RsfOY)Q`D!_w;YFR@whAvKKgbN~vn2TVdj{nIgBc^?g)GICHfDc8o0*N8% z$e2RNq&i@wheuu9go~SqkaYKGcaKdvPJ}&=#;(^57pBunx+Qv_-jgB5fYi z2}dKoIB_q}`2ID%e(&DBaPQxcFC9ivL*k@BHIkvNiAJ=X>@`9K&}l^zz(iz}Lu{su zk3@&Kl#^5oa2+UmNK$}=#<-EKd3|S>H+D7C)va#sY<923T6F^eRhhD#ou52&2m3Q9 zm$&-c!L_%;>{NqETCSp^9_4-)ur$@6z(z2j2DjiwsDZ7ihAYUZuttK@KO|rcNL%K$ z4Zgs1!jmbdW4<`y>lgg$Ex&ok_ixKJO6HxZ=3_11{CS zXNegrzK_A*BUdEm%<_Z@V;679sAPgZn!ADmT0`APf_DJX7(=Vt(3${R0E;L{27VvF z+U2#ATpL1Faa{5Il%pz zy&P$r*|kj&9R&}B|PH`wJktY9^SJmS(SgFP5Uz` zS2NkY+$1uWX?^pxp~9qPEqJH`TDUG*0DwK7EZT5VFA>Ig&A@lP2ceNI_z^xZXn`BmZ{y8Pnl zKu@VTSb!B(8(ms1GXDxv!eRg4U5kau`({k8pOv z>6ow1`2Mx~<~`rM;QKeOd<9GjL@^Q!)<7-HWUD9#%IRL?pd(yK9X9IyMUP|1^0vez zmx`<3YLb)f6k<-XJ1S9*oUfpgqXI3^Iy4RrD84tlvce(U8XWk_c>uvsH>hhB4xV@M=sB|bsKmi5X7695B*dht4nJqBVwZA87?D+1? zQ>(AlO9xWTr~)S8fB=&WXr)!#u5>-u<&9mhyebd2*8 zzCL#!Uh$g`?k~RLS8v_fYv*60C_G(MZa1;K8lvE!;;(L^NCIDd4z$sI-E)yBE z0Dy%cm=RJFxDHzDX0GcyyS&lW+-~NW-P^-5iIIGPs)YAEKmB8F#80p|$Lo9PPI#Au zS((SmS(rwSje%@hjpl}5Q`2|kSP<#W3I!?@v`y+|0rSN{`ip{NMi%<=NTj z{KUDjP_=*%wrW8TMKs*%q+Xq%G4tT@5}Kf;mp=Ba6CxZY>L}+(Kqdh)w8S!O6G95} z2VBnVX0H23JuG*{NF)9U8aDm8m-`1?-P-$iMc%JYz6`-*+~@%$SP~B+pz`ZTVN8)f zX#s=PDqhkrb^d_2#pt$OZbb(#fuUA_j0h1>kZ<_u9+z*SNv*c4Q0|#<^Vm!tI$AMQQrbaT(Q^CP0+j@<^_Qdt^+Y!FAf0*n^Dph?@njkXJ1&F%XeyPDxDN|GM5+Nf>e3shAmZPj3P z_V#=4IS$rw4}cCg`)IXicA~5QR7XdLF?{mO#_xgYQ`N8w3Wu{eIyMdFayYiV5e~-S z9DKp+nGwiBQe?rRAg}}rD#(o(9_25jZx<|HbeFKi9)W@hpg%)-!jmOF6#lV|zPjnA zi5ww9%(}SDeqV)%DD-=Qg|7{i8NDFjW5U-P*dHer9Ed zYw^tCJG!7xt@S5+$Dj)xR33J*H{iX^8-6`{JoX0GVamg?9i7Wjs69H7;mCKv+Iw8i zgh%5`mm}T|G#n%}W}%xqUEbR4UYB#bnQMNZcx`0TR+X%(rw{kx5*$7`dcODS?;Yj6T|BIO z`1Kh`8a_D$ou@$gD0n?Ho~Mu~54tV`d7HQMTU<2QthHR>ai#TIn=K+j8dn&VKS$;N&!Pz4}3IoP) z1r$(JXaPSHzTV1}J=dO^L6;($0Mvpb|G)lkZQDwL-lOuEjyM@%GG^i6h{$1mDL={5 zUkqz$(y2+Hbnge8V@U*6+s z`N$&ABrcUCjwu5HjfgeS7_>&%0(J>%SV&!B% z>xXsWwV7fZ$KyeHLfw4O9i1xHPBpb||FPkvF7vp?lUmb*pPy{!f$8SJO7nwM=V=@t zRL99Cvw~*T>8?$H+eZ?eVA{ zS9CB$Rzbf;t%1=t{Q6n;6&2D`jy9#=24(?Jz|FKmy)%4Hu2cd>&xqWFRGA$m5U6|0 zlapQFf0y{zojiVfKt+x(JF4g;=nq~Ue}A%S9sPN-&Gi~M7y&*0mVHb-u`GX({ylL$ zF$*WVU!H%v*KePqO(vRxJ|S8=&t+Q&z3i(jKFAD0`h3J-HRO*aX*%xh(_MOOc7No{ z=7i1+v(CIVmSQL38j?tr&Qz%is4NcC%KdTulf2r7U%xo3(Iz86 zvXtRhA=nIrTD-$nvTshj%N$-ZB?p-X>!3>xseb_LkBeaT<(nQpxn)< zC6B#xnqAPJo&R;mzx)iJ?34H;{_I*yJ8VnbXbAt5GE@9#Tgx2B? zlIruqnnknP%Ee&jfdJj0-9}-Btz@e|fc3`(Krnv)Dt(j9%E9&$uH2A9tA%M@>^56z zpy{<+v0eLaD`dXWuwl285B8S@Ui;(vFYg+Y#PmD;`i1}N|Mmasb-+qY7me=2fivSX zLM&xp6-0L(?3HyJw!PMC+J(&q=DqrF^6ekM`X_cJB&CFmo1T8B5IA4htLOI5--p*P zrl@#hpmGD)ac%^7fNvUlwO7IZWy2iZ2O>D^;LdG{{qIBkqAGzveI1*JpD=Jf?Sgvz z!FPXhTmp|5^0XEFM@nYDsDsjeu(ij{(BpqTU1nX>$?^YgQRP=-*6Lwk(`(b`aKkUJ z?CtCD;Wm7D6F$7QcekOvHs&SORNf4d)t^buN=J^P=xG{Qpo?Iw(Y9=Nwh@A74T{>R z$-6>3YTWUkdgR`!&L5YcqXFWPS$qP)$Aur@{$%+a{lt#GxBce_f9jE@9+z3a8vi_) z`(qc0l(3UpKmhChI~~ z^1qk@n!@6xGD~D(H8Xr}%oKZ?i$H5E{*Ik1#B}zMFzEMxV?BB_YO+ps_=&lHY`hNo zKRSdwJ*oAt#-a|;eDZ_i**%z*`pIKptv~+3LTvy0g8~n__E;N^7s>HY_Q5*kKhwzC z2&StGd-cNJ-!va??ahsScpWZYg&aL5l~5dqQ(z#XMyHn)K`+TyRF(7?RSd( zCr^`slWW6{>gAD&PX6$C!RS;Q4k+^;~@cuOUIevBW2}gr_a`_(r_A%;WM>BEolc!tt1ddMv<+#{m1Uy#k@vHByBBqs` zkp!EbO`l>v>3m@?uIz6=hW9u2@u%?dHe9_j&Vw~MR7c5pSAr?Jm6}W9sj1=F`6UxG zm4vG$KBJAllG~-}chuV8ZeLv=)-;VNCC)SR2U|N2Y8ngSyLzlA7~hW!&||-P3^9LE zQU4*XzoO)Se~p=PGB}&9Ha$b6ZFF^Ew=crG*UkG|`*<7P-`f0nuro9rV#s+FQ-jR+ zmT+lhp+Fxs zM`i_o66e1Y-lfebU?~pY!gT!yu>M0{!N79L>dlsZV2ElPySm`rO?ZFXe7LnYFT#g6 zHoFd-VOAWXLg82`2vy1dD+ep-g$hwBqS6@bQIzl=jI=7Tv{Y=OR1qZgSnm%THcPGV zw7mEJL993*^vd{YhAps{rc1a8c8-?#3Y**Zo422xU+yft!-5z9Mfw3uT!ue@_3!f< zS=*@Bc4yYvtLOI1TYGmCKHi28Z|wDpz?Cs0WfRIfw2}}Hq{tX{dM`~!rb$H~04n)~ ztpqzy{7tfy>1@QB33j4a+^||}edo&uZ6CB-s@dpS6xAx&pbMXSs z%mO)M;uOKB5Ts0dq>;ox*{CIml{nesCT^6+^RG!Srd(GJQMUE%n&ncfJ68AJFQv7* zt**q+EzLPxnY}e#7-ulYTBF5UqZ3Vuh+R^!>l{dVqhTqn-4;8T3lT-6X_8vwvDCfe z&GDA7{>DyycA!Vt*Xd0+2XDVy%W;#QXkJ~*$z5%&D|xyN)io+4he2uo?{00&)%gUY zkGJ9N>(E>q^R!nhjYu=($Yegohb<1~Q*-vTIMq~4ikYI5+6PtQo4u^pa>epN>pNfH z)30P}o$vG_@O}HNP*5-{?9ZhRKz;Djg19lEc zeLqGSJqfI1xSZ5K?e}|nYeyd+%&t}cj@L0_j#XuLq;J$wYxVdyPjv8P1NM#og|*rF zf*05J%UgS2%x$k<1+LI&A+f@bQc;<6mHC+ra$_R2I;GjDkepJG6uBv}!~mE}l6Ei< zH(IZ?{N_pA)=Ht*MdYU8*#cd_E8{Y7j&(3g87`rPc8q?esm`okDBDI#$~MFp7psGY z-A>^*>E}<@-C!-2Y(%+lZM|3yNBI~ikHO_YZBm!^7|l4iYBU^FaSXC2Xw@+~pQv~L zn@o`WkhNW1*sJUC{}?0RZYm98S_>#QMQz3 zIA>5xau?BwSU+qwwtCR&-dA_(ALMH-wt5CXcQog4ZFXr~n9iZaI-`Nsn2Dy0QBXD7 z${~}dBdeu5I~ZU9b2?)I^T2Ztzo8Zh`zp4sc1w6>Nt+)<2>Yz?V9*cd*!I!$7#unJ z=3sUYzBwG^ql!*G)X_KNXZ81oGkdraLdXOuTSzM7|Icrdg~Wb(*}S{8_Bv>;$v9FJ zlO^xenC&IkF$T3R6G)Y%aZC$1#=b7ELXpo?Oq)Lq-=Ee*bf!1gOvvU?dbMfEN{ZH}N zK4M7fWp~bEgI&u|Bu3KswBso6&2Ffu#W5g$T*%2+CtGXB77_uY zh2-6h{ro0;ybV{+jmtnIGaF-TrgUXV6{(UHrc^d^i!^L1d?n+ibS2H?!~sjxAW2G_ zf41~nUoEw|S5g7^O3hZ+&fA9Bxp4tEW|yWjm|>mKL6c3?>kX-rXkc5WWvCgq(7ff$TOcZJ8Y z%VTr-kGXyjE`RM+XRY4!+H4u3La?g~ZeN7AufxaN=KZaGxV8CpU}4r2LB}etpR_?m z{?YiH8XY{)lZhsd^OxQX#!f^*B1nn6HMB+-nE%ZD1G5jzZs}gp-q2h!rZ{B) zM}Ok%U(9CK2U#FG`XYvV+L%ae)7maz{FmkPerO@-ao4P~=g;hyw`tcFKD@TKuY+Bh zW(f&qNTCpz1glYjU9GB;siQCu8~>-A7lw+llw7f><5l za6af|!^b&xj^3JG2F}pjtQAQ}np1)e=Uhj*QR(s{ff$f6h{1_q;w&&T&Wt9|7_$rJ zADMq(_MX`--7W1k;T!@QP>&pK@oiCuI2iiE+24d07QU5Z7GIqn3tK{K#YBMqGuLB{ zNUB-evva$-w!i(*ynh`&z6l?0?fEOyg;}6z8H@Fwa@cp0Sc}k|a8nY&r0S};2HA0- z(4vCGL^yd*~~IRLBxQJwTfN)Bp>ecZ6XGva}2Q}SUq7O(P$wF?{DnGo7fwhv;Y7g07*naRPg>bynb$61v+7&a%ux$49;WNI5<-c z8BOD!t_acvK_#(l4yTfzB}yTSQg3k+R~we!e6`f}f!%|LEzbtMZaJT03)4HZXT~|q zEm=qiFgI(!CZ+ZyBxM4TWTRpMOiFFk*#^{XM3l=}q-7HF?rqN_pB=N#ARM91-g+Hn1$eN=hP32v>DP&N& z6ruOc%95#5zS6Ac+Vx9ENEVU{bP>2n!B@;0Xvv~dZ6PTwDItKy#+F8=qc1KZ zbg+OnaF&!(f%!XTADO?WyE$TQJLJfT4?xA*4B}qZ`PxU44jxu-1-iG3ziYz3@`hm6 z1o=vA9qSZnT6PUQ>ht~|<=Qj;_iVOuSz>LIq3t%jy9v#6V`c>lIYCw4fPs1OO{?}n z<34S?C5NkgSZZY!Pdc$w5l=WuIwVLCGw*sNGc zQaqnMg2c3sEHc|3m|S?-wFUB)Z3a!C4a^qIKQjNw>>aaP+E*288}NvZS=*1nh?*cB ze^5U$ObkoT|E7Ir_RY)`jJOg1PP`pV3T5>FqXO%Qwc#S2A^GKPcy|*%ytWT-?Db9H zS)eTj`jX`ZpyV!3-AtE?ATo;N8AdjuMir?l8#+otB1>$+*}*Oj8?86VWUJMk`&yyb zvw@q2iv@N8FN2=JxiM4GYhyE`5uwy&qexVEi;V(!ZZ&R1*a?Rq&Ww)2+5)o|W!LtW z*=yR@wAX|)0PQksOVQ6xur_-fD91=ThEc8WvQl^{Q(G1vcx&w!;{gi{JK4Q+;|i8` z4rc!$cZDH<%JImd9FH`vF0%3er&rl(d-2?K5z?#}C`{VvOr6RqS5al>r<3zY8jx}8 zl#N~`C3{Alax7mVBf3RXA2(}Tt$g)Os|WQD(pvMap25eC_6%K{Jqx;k^HCd#B|}@2 zY$HY(H%&#{c43zHZX=b7q8EUZEhL?B5opsQwo#2F)Z+Y;4#KE%s0d&nTSypZC!uPzkca?t z=#0711iHZdoY{Nk?~5Ag4ed*sbFhlFZDD%^qiXX%!D9dZI|PY|wT--ZZ=alCH1^aH z+2&wm^WoX8`1@!^kb!OCn-eSq2r_H`QNS9Jl=RyDAmDs%FRtuw-#71Gho6%g>AC4L z5Yi@V^!p!oJ=J-mQRP@D&`66WDmBiY#{5%xl#5a!<16X4cNt#-q; z>!*grndw~I7|(*8!OZBe8H6Nj>(D|{?+3>9>;-3nEu{RTK`auC8d~TY=C7ImOn1xt zrz~rCFR(cX4TZI>rdiK4;|F$a-zP{1B~Za=Tp{}viPK~+{p(A6_IH85cOyXCB%+t?IBnye1p%4lwDMEKq$&@ZuTy8S#=I)OD z8^0}*Ru-j_sAMBPT@0f4)tc3k)tzsb+TP38TJ(Ajzsv|{rsr^BB)_)KEbafBf}{k^ zaKhiN{8j0;hZN@3z_ZErJkUTF_ADgbt72=r#5#!h4h?wn97xlaXY7=O#3>{_Eo&bL zNpXL>P;08}6f{rXSICO@od0ci_P;C}fybN3~vW0|afrTX%qP5T%b7kkDN!g$Y&#{bPtzN1vBmox2EO3@w z35?kVi(i<%Xa0`v4c$xHS2SnPpk3bo@3A&jwC4ax`=o*&VQt4qs%577SK|tK8b(xY zt69H+h-=P%=2d7v7kc2bJJcg#4f|n?iYFOj+pFJYf^>OqudeNH--mav?Uy&nMl0D+*QV$VY{wC)+;*Y`x$5>Ot!VmUq5+kgwHk zxs={EEY8q{*-xf(<1EYC))qBVTalYQP(|@r4?2~T@HQE-lks0HLiTNH1M^$vKQnvF z>@Bl5bT_n@*nH1Il4DkNGNbP>pmTt?Q^jN?kwB+G;VYqSn)m7Zx%?vrrJ<3G=f#?7T6T zD9RoK{+-KcmutPcvbV47{Z07gP5AiQKE7$@FRfe9wxMgYz&#Nhr>u05@i86SRwe*8 ziQ-QAtdJ+D`cQ=NC<%JSjh0)j?|pg4=E37qVXKRQo0bnVI5S=v&n&xax0Wp=v*H(9 z#}^-65qvyZK8+4Cc1q451elA?I2*l@UNHa2>^3VTXQPEglNxELW$nLOA^Ba_MunBS zoN9acR}!SEsfjh`<#)_CN4i@c$BkY5Gx4uI6crsH0yYflH?;rb|Iod@30E)e{5+g5 zY_Y&+cxTD78KtIx)kf_SjHmji4)5(e_GI3oNhktVq7beWY^h&qxni^O)txr?^0nr@ zE}f4Z&6)8Gy*18^Gc?O;Bs!y&8XFJQP4e5pIhXNWnu1>0udOo{$}}l?BQ1W)YNYNa z%i8TVHUrN-BtGDG)wtJ4vZv;CuRSLxSo<_%l)!BNSAP+bj#N|mD)dRQtba|DH_c`R zqp|P;I7vR-`Pn~jntvTLkCDq_7Aq_Swewd&=jOqb0+G1MO^)0=xm;SdxQ|#W?PGEq zIS}sht!(6UN~U<&>S3eRgRk#cKlpAXZ8+cQW#H`$E#S3rW?TdoXqGbSL1%O&=P*?I z>w{6Po(m$GyNwo-bE9eKjM;+uhoVM$O_wYr=MW%fHBu}Vl4%zGL`8qSg{1m(9LN(N z=x~Bh-ybNVMre}nC(nh7tky_+suz3e2}#;5HT%gf@Gn_sS5p{?7Ekm8i={D%BDyRC z3WW(bHYEYp-U>=?yf!(2n@c<5##bxW_r6?e^-bEywt5yT3(2dX3$wYgC|;4B(Tc_< zceXtw744a-U7K<**a;b}m>ILc0-8Vv^XJTeX8xYp8)k3lUKPK#nza=RNgmT5BS??V zLH$iF$0TY|UslQ5V}Va%m+^yRVapzA(*Bn^Zty;uYa%xdNNJvn*-PGq_E*J%6O z|`#FN0;(P|6r!5E2n)$wCsiNV_&;{+9Vi=5Lw3snkdf zc-A3##P6#iPGfUw4#v?e^!M@ksGroVE$$PfG(!gr=rQc-=*cN@d*W#OOf#49wA3|< zS;xJoue=^4xkvAwYi@4zWQR!??sB*0iZ+WMUO1hnHl!rM#lzAtPhkWt6Ewk24DwzN zE4E9P55Boqzb35wJo3_bKSvAf)|8MmC%gT((4jV&*pd`v(*})+aF$_AO|)zwNpqA~ z+uY~^tueb`_Llhv=5Lw3W_EkRLJ~K5ynckWokY(ek2|<3Mz%LcRza46q$6&4c%!46 zsv=Qww7&vLK4EgILHmE#?9U5!^J~fF?iV-mKRMag&_#4>UK!6rn}9AXQLuz0Io{&X zlt4LhXE3(D+i11YW|{o|@vYi5SJIn?`8hU6FSA}-9kk@le}|HL4c!gh^U6XJkuxl-A$}4`s_q}XnI>(24w84ofvmlM z^L<(S;GrE(vuXv^Q5U`gBak!MD5}z?GHf5IPqAakC24qI@zKJcVj@7UCeZ#AxCv}& zB}me`K;{aPM(mP~}_ zkmQ#6NP>2nqnTw#+r-+smc>UFZ<)QPdzr=U=GmTwWJqeHG|Ep3B5BJ*J>O5u+9NS2 zwC9+JOziJ?8+fw(CnTg%);_5HFCs`~Pc^CF{eVDKq%I-J;CN(Cv^l{@zSQhpw@638 zk&F&~4D@Jr-e$=~t|7>z?QkFYy47yOZY6rPTSnH#8Qj8!CHkT4J(ACmRJ*qQu{ynL zGop&|-?ADhooj22`33V2EZ#AD%j}l!1?@|kML9!~^xDNjawuzaIH|`3>4fN7Uq{kL zeqhs+`pdt7ARSCC)8^y5i{a5SDJ9fYGgd-3)&8Sm$~p!8JwjE}yy{lSUB1@*#$N{e z>UNxqS3y6uc6LE~Vc|95!ZbIf1OLf;B(b?e80plQ z6yGMxNv( zha_cN_S$X(UBlu7^S8|3F}umEEiB6M$f06wwXA)LDeO(dNjg;R=?;-J4)?TE$OYQ5 zobfbkJC=~fUnL}snu}8^j2}f(&DW|1?=d?SBptNsxQa5EhlP(t#p-z}GsO^e$)h~7 zXe{oc@WjA(XxQQ2es~@(ueiK2F44ktZd#x-G&Aq;lwcqf#y`p0^%$1E?O<1Wa+db| zMrSm}{F=oZW^bAQ#OyWQYr4_Vc0fC%Bi2@r)OTfVop@U#X|J#&xi!b9t|TNqJtgeV z+1_yMv$j!3wcm>qoMDM~_RN z|K$ANL(*7{2VLD;Uc*JurF3r0(HzZ8trUJ9Y%B!rc4&iK zlDAr*xs;AamlJa$t58+z*P}5w(J(PHI%5tYWaIzhE%SHGf1-QI?1t`Tv5+J+Qk>3^ z=wO(hAV{aA$P*IAC_|)isR($s64WGpEcn!#p6+$PJ`dqCd8(Y^l|Q&^%hx_z+$*g{ zl0A<8aa5UPUTMGC+Fu^=uxEQ)+Pc6KEt9!O^7~6&(|l!UY}mN^veH<4#G0$6)TyU? z{XR(*4of(=uO%Ma)ADmb_KpbBUOP_~c+d;0Y&h&gx%_!JuSYa5bK9DN)>}gM@fxSE z4$8^5$L#pvns{YJv83)w&x8HF+!Wo|!dlGxcG8FrxXB~l;dAo~!rXjjv}SW-{w5Qo zgrr1}#^aG5@rWNQB%huGPE5lIBpt4-!~Rc0tkd6U^7@$uNcDbp-1Co`+FEmQ(0;xH z&BOgy-HZCF`M#bcNXN?h15no8FJ(>~&5mk6>9cjvwu74q!BXQE)C3nE&V6PsV!(yA z@(teZT5XU?ooQj3!@21!=zOkcZ*}%o7jLz=VRl3Jf@YDIT4HU(F>5P7za#mw2d^x=c@@)xi4*1FIw^L1kF|}8Rz~*(_S!l9Nd+Scl8z?3ZvP1+)!)ZQ zesXY)z&-YqKl$Y&HCjVxu?9rkYV%p^KWq7`9=>Vq`hNweG0Z(e@5G_idC+yx^_8w} zb$R2@ZvFDcFK+zog?DGxfXRpxYdf@%*inO?MAB)3bRclV!}lj3dy1JFr)GUrju=pd ztg5K#NMr1Ag0y$)wO+>uQ!;+E-{z-S+ezNX{s+~be=K~GjSe`&NoCtXfyW=zNSYA> z)}kG|d-`vze`Eb;HXAnXpX7J)HL=GB_njHUKroZbcW6+n+yCC5-}v>7KfCtxGhehm zGnx4?I7{+#<9yHc+ za=Wls$6>EIW{4;EAIC^KSg4g|QU`-p1W@ zKD#}jT|QC&rhJ7Y|5wO5rXMHg;ISbd|I*Qys{a27Cpx4> z2fwQ3`w(PD)3aBL?|)?{TD8ZZf4mAEy`sP<{adj%)gK&S_Xk;9%}n;Ci_l^L>rl_` zfz4+&zq0;~^-f#a7wKMXlv|XTerq;I3u9g!Rmy=mfrO&Tm8YcvO32(E?bNyt^1;=} zJv-UN=n)Tij7A{X9>sce|9^6b#|JtZW#4~$P}=eS{4tuRGVYHrZXQ9>-k#tH{Z@Z} z?3CftPwgqKPgP?*0!O5MD(y#D+W|Qo^&~Qt>o@eSwo*f!OC6k?iP%Q zY#&*ChbEn7SVydFZ^U_WRt{>6r@nn!f;n#RF>v=`cM_>-?kA)k^YrmnS#-wkGaqZ|WS!~UELnuxK!B!- z-Dwmm5uB*USH;HW4b|3yvk+lccT^*lW9xG10aY!4WE4e#dF2 zu3az&j4WGP0@l#906x&)(ciK8jn%KL2W`X`co2O)!(u`KTH_p>nP!&KQm9EisH5eS z%F}?1M-4_9cm+M$$%SJca#Sx*!7_po6r+X>trX=^o)IQBNu`QRaxtEhuZC$)-1$Ky z9WCz1hVn-wm_veeM1C|JG>=H5b0P&De%$Nq{7a8 znpBMqY6vY&Kt|lqe_`{9?H5*Gv~eE9SFx7uND~g}oGr|;c~DoJodOD!*xh9)(xCy! zed)oH%p{s|rVNRlvCYow8q7CRFYo0Wd@Ia?!Q7IcWm`1!(G@b$@+QS=Gz;beGdm_T ze;sSHy;&LmJOm1d#6t&9M#@ohrNS6S(BwZ)A+>Jvq}kdhb<@0#+tRW8bnNaxZM%=% z!(JR*rIAzZ)gWadsX>w5itX=gKC%9d%~tEkH*qgE5Sctp+Pr16bo#Q(_|PG@Nr6cA zBGKha2a)_sl2<_kowDzpi*I5jw$gwG`C#t2%VBVqSUQR0t!aj!btg&e^57mMQ#6m} zDW;-1S;YD-L3;Z7acyBe&Bt%kCr7$8IORV282XQ9eL5tq>VxsgfzH-4ff@V$l%8TN z=OEB%U0xtGSceH8=lO~^s< z``q7JLPH1$;+E}KHlJAk&iYdh`u zBe0bEE5&a%3|7(#)+s7;J}f?I4}+~S??`dgQfQUA2#&}6xr(D7f{nEG*5lybc#IxH zrd3fs$O$GHu~4ihkaRQ?-}Qn$WNrK3AN4r`(c` zFYU)9E`_AuhL#Yp1|JyKY(Hfrt+v`Y-<+j#(6+IL7G~JY)Rio7P3g+GWt$paPRNCV ztx!a?6is6b{nTA;#9+jfb_H!^LwOl22J6Mp(LfuplFG|m8bG+C9m5V~T|;%Ku*Wx& zY?QHr6x@Sra5XNuf+++ApVXr@yWSVc|1#EgfYIZV!3UomlPgXMcD1-e^^=685uGEF z4kVg%!PQPfDG$tnEej{ctQ7dD?+ebU-(g}yo~5lPdD$}xhoM373PlZG5B zbCl<-Xeal^sX!+^4>{v$K1Uv5g_39=VF!aOB3CNU&33{17BeiRA4me*cz7{+zC?K> z?j*;gq_$se<%S0HMLy`|g9O&ndW;@pj3EvdTYP9^AQm%@fgQ+L{GhCT^l%*aJswei zFtWc>gh9u+JjvP)_6Hp45snMwkhPt-4pg8AtgV??+pwYk%=#0XuWTN)a+c!GZHsoP zbIRMUneohQUZ_zKlJ@vlqC+OU#I01ReQZJ&kA#(=h-;C~^X=T$sL%V2)bMER0JS{Z(-(kHpTB=lc-xU@bZ9OArB6 z?o*~?%aL3T3DQZ{_Bg*dJte*mg8k|L4r@EEpq68F0wSkd_+5f@{DI|NTQZV(WVdAd zjrAwipV&mL-9AgJ6xjwOa}-Ps%&VXawBFfDi0aOfpw%1Xu)PW?UHO#xvs@rL$F?N#<+m z+}i&q`mc&T3K_c&8fZ~O4T=!5lqkm=Jmkm?R%Hv>o7$7plUCJ%qM2&S>+c_9Z8~~; zFuPAsuaiId0VJIu!{ss~bXX{??Sah~)}LA5Y2|F3-(e*N%#~?MQb^9RnK3VsP7^^2 z2Zism`U5&1VM$hXS~?6|wsiyCxi*EhjpH0_YYYL*8pA^3vEV432aZ`%vJ@`*#`w&zm38D`#6ml@kWTGcKBKG=G}SO14tg!1 zjNc!#wt7;ZkdSJe>G0X~cTLhx>DEsm=|@;w&3MWcnoN)+h7FrLHeXnOVZGMM`QqGT zYmy|JQ>>)3yJu!+m8_kuw#V%p7n{+G9TQ?kZ$yJ#-l-`?8|Y;_DVFd|NuL>rX1(DC z!Gc+v!qKU)U56+oW8DCeHpi>Nj76|~5az|j(gQ@M4kjcSKCoM}yknij?WmRbB<^L~yvN9~0b4XPJCo*lCTL8Q2{_#6 z94!Nc!$z#hde=0Ot!O~TgY-r8SQIg>M3V`#ozVw#2#vKx8;Ww03mYWIg%2Vry2+8n z=M8GmKy*T&y$bp{*h`csYw}-6n}$)^Q~57WL#h-C)KQQpJxG+230DGQRBJZmwCOoY zX=AvB#>_0pFbg)N@Ehq=o~rK&NufrSrnNgsT>Ej0YAC2g5KOY4_G_=dA3;90a9s3^ zg-<}5BqV|NZ13ssS$$)3rK2AS|EMPkv?q}AO^-DcP`+CMQ23eA}Bh&F3P z-LefRoe7IkW{R#Rl;b4(n?V)wpi0K9R_KJMnjlSIrB@Z_ojB(~B;&MJJQp}lv-Z2{ zM`GFD6$lMYgGYu9+XvQPS$(13v2=YEUzC`Rxw6r3hUUhiu(r|GRtrfg=o#xB$#Y_4 zt0YQ`u28jPY1v9h-0K{lYiL_=|Qfqss)5*C`mR9*C#qgYhhQA5!h1hZg-Kxfbz z3%E8e%ogYho11rL9SUdz!DjzpajOuDnsg{i`C2Zp!)S2h!pIVn7XfN+ozeIB0^Ee$ zX=M>CMP^U`VoFRf{a2y2rZIWnJ(eXn8SROk)^$8 zxm3||EQd1*Ok{23#RII%q6clo9XXaxlAN|MW^)E{n4>Quc?o30lAvyfj|GJZN6FpGggt!u~WV>#$g(-stF;ny-qA)%JRr|8Lcw zlYnIerD$q0sew-Fkcc}mH#MR`PMgRSi9-q$P(W68g;um9`yR<7wsh2{N&2aQvww!9 zW>{3;Pcqp-8LG7Tc6ehAA*8&y#GcIqn>*IuSg*8pzF}XaRn`qA!82KEbzyc6MYWw- z+a6eLQf=?A3)W&s5}#QlXKw zaV1X^Qb0CoKni80fO51^CfXdplCoYAOltDURZn)Joc~c{{C`1CgEUg4(J()*U+7iJ0dBaLfd>}bI0l{>m6(9Q?|9)E-U>MphXMp3KrPh zG^=*bHWn_5bVp2*N98o6B!lGZVSzy`FI4LGoqtjDmJiIA-sgcr>;N6OA!r z7ipI=hcT8`F|6CTmJ61&PYN_H^Hlo+b?8A1MuSp{MU*rdo)6fo9K!HqPp~mHHzZW~ z2$9;G>42}52IS_H>Di}dV(*{*52|vE5D1L`AK0$xA6Vb9y3<;=LcfW7W!+%vZ38oO zX(S102FV6ts8IC-hlaft>|lkgFvMBL5TF6>@QoOthaUA}hk9&45jX1>__^_h_A;Lj zU$iOZQyHmHRhY6SK`4N* zEgoeKP5MoVu&~b)PDrl_CyCWHsDZg5X(&P18cPaNq!vjE$7 z-@CA0p z6vS)f2w^H1l7CP&)($D^c9VmnPMVP&Y{a&ZpCle76+oY&#vx+Dq%M2^Y*# z{ICEks*xPJ(HNi87{Vw?twTw;#yIU{1K_I?ls*Tx-ddMP9q<6 zRC?sMhYwS<7NfK6(x3Z^Smf!Q7luCXONhd$ZyEX&x z$acl%f%QG>mDXY~B->riU zv>@1`uVP&=)I&rO8n!crbL^7l(q>n*3)(I#5)&iJEg8G((=CN8j{*?2K$AG%7?UfH z4=B%8`rMadD~m?^r033euYzfv(YE-uxS@vc{qUfeB4ec}R|>E&H4Hr|uAiWdKE&9> zNE{fN&;)`hSjtq9lQG$T#~*3(;~!WkyEZZG*sfSVuzp~*)k^jteHP1-;3$=mv_9w9 znN4b>W*mZQf$k{8S~w(`dbCB1DlrqAlBhCfsfcZHf9s1P0E;dC0>7fYBwW&6(R9`{ zB5CH6*3vS9lQf?_>}{X7TW0=bt50DnDhzN^k+9DiOGe1E*J> zO#Q`PJ1^S5w1@vJJzy&|DAS|?+zjnH+n$@BGkeMGnr6mX&K!;zvXbpQ&1w-;4v6-+ zah^7^MF=hr(!+`|YT6o4W0yW~FmVA%AGd}F7?k@`5B2w%#=+xcEf6iklCCexJRPIT zPR(-R0QG+j)}odmJZ5R6EvHn8F?f`_JcbzDiUup~3!iFk&D!F?cExth`hnG2EAb$H zhZWXKu@sO3EuU3-?Y44As(Vqp0H#gfn0BzHE>3-yBqK7r4t#xDljNzPc zN%x%g+Cs-7g%A(&QKCG6@?fl;`Tb*mKJr9JqP3kAdTdu@gF_gdY^bOymJ?%&uP{z@501ZH=>HzoQ~9-@WQD)9P}Vp+Rva+Zyq)1|F55ELKjeFy=tm zae;~5RvyfqR+76XY$?h;2dZ*sR#BSw`0AckuvQtRJ-#Mh6S* zEc@{gjJA0GPs3q3HJ1u68+Udin+p(O4Vy9*JG6$+(90zC*b426!OPgq=`SoiSGcy> zHO(1kqZ3Q<_(&G_9E&98CnJd&>Bbbr*NWk+v~AkW)dI=BppvLn{<7K$p1?#sbD{e4 zEGOai6>Ma?veh`g#^%<@8=Gfkcqv3Roi$x2Z=7*;m0yLb7FBPtO!CrUuK}02%ga_n zJ(@|PP3$kaV$|R?QA0>bYGfuKtY6z^#d^tltM$a%vZW%`np9+$W{DupD_OgiFnqtQ z6tjTC5{AMMXB9o`;U3**hor>aY=;KeV#Z($XABoKS2P#aU1M_=EeeJaEjuwxU`?cF z-{xv62lvNtxR1~$j$a)-IYt=M$0Ho~ZtCyJy6+TrNi9Rfp?MHnv!Qh4u%gkvQLTmp zi!DX2NZPdpgRpO+Hyg%XQL%)2fzjARqm~vWi&0SwQ9~rcFeAb+DDFZuwH`gj#$`_9 zBKEQA*Zy@A`xTo(TkM*80Ql&;J=K5(Y4y40S@whZ(10;)m z+5`9S0Gpyr8o-DR{Q{m7uCiXc>u3{AGDWa3(h|#QFN>XZnvN_}AI!W6W%GYPhuBBS zxQM+}RJ(GV1ZACn%?#O!h@xU+AFYBp@5}gw^$Fej2HH< zN)!pjTc5PeQ(aB z^Sq))2}YrUQK*JB{h5%F+$rZ}NJRt)o4Bz<`}0;3dEgkE4G%W@<6m2n_u4~t8yV%gR>jh zMRsD>ffxo2SnL0QxA+`i5HD!X@P+xD*b(v}p=yM6lp$e&E!ddDll3i-hyOb$ln;C}CC)Az;gvldT| zrfZ)YmSSpSXEHn1EK*q!eL@VYu&6M>y+@2VI|k2DI8+5%@oa9{2^YHuY$Yy?*&_&eytczH z2ZRx9>zaSTUKU@jqZpHH`uUy!b5z%1s|3lNVa9-g zhC!^1wes@{fu|6)PXZ2l>$y@FyRWi7gLrgn*n^WzzZ=C)$=nqOyl3bcHVi9v3v;w{ zqS1PvutwwTa>!GoQQXjMX$NF9&8Q#G-rV|xuL(K)R8_5v|T1hn$xs|ju0?Q zb`%;B9rG+#rBT)hlq5-KaeNw(grQLLfLf+Xb7?0&9!1!6(-b)-GeC;1Y)@F4q>f_% z)y?oqd{4DcvU1fUl6z_OHZZ~#F=?T_IHHDtgUxW$06x%sfeWLZDC0rb0(hYSeu`iR z@IZ*fNPx9-w!}90Ie~I?!15IMI2~hUN#8k`2Kg4>+5gwx)yM34mG$3s&i%Zf?OHID zPQi8E3IeNLk?AIGMMq*rWECqI{9^<8&|v6@(?mKU*tjZxOj$8R1MA$P41u9-;+B|A z?Mgz)65GJ!8iZj$VWd0{E!I$R z1=KX=!jU@TZL9!b*)J3ly)D(!P_0^WQM5{^BUN^I8wIMIN@Wkh7uQCD7bnQw+(5WPGcK>)dCMYF){Vd=$YqEyHF#p5qM}S$d z0#(DuHZ-MnBqGSenVJFehj}EBf)OL4hMh-LNaB213zTF_<`Qhk#|I=h3wCfNOOz9H z0cPmjfQFQ@Xo$bs7Re$I3c#)MLR{2RN@JEmU#w>$dg(%BbRB~hr&cu!;B^ zncD^mBKwH~->k~#JXxcu8PtAFb2XdM(YA3lrpXjwz$)y!TE+#UIbeEX$1H9zcmNFU z13fTcOW<2f>xq|vvw@pRas%|)>kte$|Lnw7MnZrvoF@!KnyrO%G-4EP$WFl$tb$P> zjiCY)v_e7D1GK8d0@?X8BVaAa0D?xQZJ?S$BUJ>y{v6{Ga4^AB+XOUcVx=Ej61L@S zSpdzqjvlShs8(_~ru{X-95yoy10Z970cZf|$q-8fiBYgiJ2?;wjKF}I7)b;aMNd2Y@D zTWBgW%9M7e)@+&S%wk^4mDVEv*I0a-fRgqnW0hfsh9x+_S)dP^Ne|67n5;@hzybsC z5~v4o;Bk>NEWJ=Gzgm!1zVwQSjn$*K5ovd{#p0!;mEo&!H$+^(8AkC{A7;h!s_h zftTmKuGLmi)V}I;h){rtje@fv2b4?$!c3Bj!m>2M^d)CKN{|Uk_aH13`PDEiFcKw+ z6wo&;+US%b(lo7tds2@~&%1F`0go#hOx50pH82|1zyglgDcM^QRga*RH?@}vPOcWG z;884Vq5ug?Ov`GPuW|dDk@uT{xs25rl17i8rE!29V^=cg0ohc)Tdptb0$ie@{$iM z!xH^(wLi=QGvwxprU|oQQ#hNI zn@NMlmD8f+092;m!b*~x-L(bVL}ui`-9%*z^hxki6QBq}gnCqp$0fp2D4+)D zmpoelvqTcP1bGD3j`6M#%fWKMDhCT#EtYqJ72{#>4e8CVyJp+#d%a#!6h%=?ty!On z>9QV&2GhN4kS3UOG=A^ILks93lUC?~O9W4#73Wo!TC)|c8KX{;v81l%cBN_U*jQ-q zbxH+WO4HszSeA^wctKL7|Cc;u5YSW5Dnx!4@kq%{Bo!C)_{U&Nq4#f-Q#tB`EfH07vYqhx4%9S;$KQ?uxJveX4t0_r&4SFILNuLwhn zVV|c61$l9s3c@r^6`&q*4_3wKCZ1ioL<8|qz{y^mf@K)TWQd#(>M;i5++~Qx-b~?% zCXv%z7{|&$X@EUyn;d72KW`UyQWb&N8jQkB=Y!%Uw8A)KfPzyzaKl?Y}@LXTT9|r(%6}8OlluW!4fYqFT*Oe8c>P) zOp^g$H@85*MD}Dd1bGLW)StI#95cOL~)E91{-C2sU?0N(W7d znImR}5impGSzwl^fYsTskC}q>W0g{Y3qTVa<{)^1>hsl_Jrb%o8XP1vIagP3C{SFZ zprAA?lV>y%%SHQ`V1EBqLc>|iCh~h6df{cR8r-T``A9dHIEO>RL_J~$XT;pVRP>-q zV*!8}iimpzJyV(iN>1uU6ysWFTbdEE3Y8#RLxf7G3@uNwt!lHYvbI8tFc(Y6AJPjB zz4Yd?e1MpNd=h`5bK0Jo*xf3HyZEonqar9CF|=ct>R@u01Rt|UOkz~ZXneH_g$v?2 zB`zXRAckTh2M38JnXDkuSPd+vsuFi2%_LA|FD>^7RA|-~2G%l#NQXkJT#rc2@MsyP z;a^^ds!{EzmZ~O`LQP(pG<71JWfq5yR{nl&+sGn=+-n_sj3?3u5B>-p1~z?L0ZRrHukc2I1^ z0P~V7H@);_o2Zy(J3RUHzr>;_96n9(Eb5!Y;On;U=r1jP?bHe6z}4!cFcNXfsx4!L z1;v00PL0&~H$p1!JKsVeG!pd#Lurfs7^9}06G+KL-AtCq2G*YEO-CS9!N^`w9TE~{ zDC`R>%g#mVKOLe}S*c13WR)Wm^qi642!#t{#v$#CHoolVfAQ^FE2WiI>hqtx7gnOb zlNTBrQ1f+4~QC z{O;5S@e8-z`I3!SN$daV@rUQuuDk1l2d>_-P0~Me?C`yNc3oInBnzty3XPFvF5kTC zu3vh~Z^vQj|!tkde`P8O61W^RBAOxAoCh`Xb{Vv(*Vw-&7u%R0Fi{mpg zY^$vH;$x^Z?1-F$vffybZ#-s)1fnp6d?yo8qzl~8tJDr)XeXju#fw!yokZdUiR)(~ zW(f#+VgkK(i|#2D8d^gWw4&Esf73_)>`*k2zU60UV8G0z>Cn^F#>2sf@85aHUFjtC zmlp5cv+JIHkIpAT41hhmZ#`LQUbJuj6I-|Mke4SPf9S@Wch=HB`S?R0+xKpOp^2tu zcrs0OB>=(DLisu@24N!+gg4C z6KadA)Q=}jD*E^Za32JwIYnzjaPLmGwQ@Z?0+zwwaqOUb#Oq1IEw2co6>`uDy<%ud z&eNAewCYcdX2=P!T;Jb$$6Y7?^>8|)^J~`c+y7*LX>lRvIM-cY&1LP-z^%94*U{4YPoydeM_Yb=a+l z)pKx%tq%+ZLY%{=K6>{TK6n2W8`gj3u@~aUv(KO2{m$3*mli*;=Yd;qyHh@H-M-_I z2iqGO)8BgAogdiqfP9=^vwriIZ40MPFayqQ-TsD{e&N{@_kM5}fUiA!;+}nv%Ey~- z+4-rD|8CbHESx&=$b);29ythL^OkLb{Z%hMe)qzuYA;uc=Zi`^S~c= zjbNUB#qnQxg4*^Ob9IQk{7wk!KG&zEwVF;Op`eqApDIAcpY5Hb%+|k%Kn;UMb6v5= z>zXa@iNAjc2L1DkDZTm9Q-_3>96fTVtvz-4(o+XR`h)5Bip*HP`kJ4N-;aLrp!7OT zzhUF1`8DfW%kM8O?%927OuTUFM2$KwoH~B;*x|F!pKcD|+wR!&idWsxO=)WBSEfm; zfi{aN0Xjsc@cMo8)YwJUqeC%%@4C&9waQ|X0#bX<1khhvRE@Ywkwc{{GKDk<8G!!M zxiRSpfaT4bw^g5PBUl#zVDpyiTdO;I+b8Wzh!L36{}lajh(<=e)nKv+}?|7m*fl- z%NOf7bBbSG=`m<65HCwu8JU{pXTG$P!>TZg+N)ks0U8Hg$!azN4@&6|?0H~jc5PRJ z%T(iJKvW4pw$|w}ifY#v_Ma6<5eu2HA!H`SG##C#d@{MZzzkN}xLT@{w(d?kjQ{`( zAW1|)RI@Fx$=D35f{BOzGV`LWxR+je^*i79$KkCeR$oSeeG*_4fx5()a_!cJ*0t9L zQ85$_TPFDGQ71Q9!~w7&8eaNWdIuT>NVzKwP_tFyO;)y~?J6Siy}R6?R@9GH0nrLB z^{jMUf;av0Z(V=G8v#r(e`rfE2{)WzSb~ATD~9F8ttiSKgJLMAc#o`cOW7rx%qE1U z9-_LlqfEXJfMv!qW57Xp*K%r1s+nx!Rfkb;d{=436cYl~-c~B~QcX>-c-2p@R6^34 zNekx*(lpA}fcX2x!(MfsGhUD5>c^-A6oB{%x^QN76#uAtE(0tt!Cj?Cv623qW6 zn#(T+#^z;!h*St#8F#jInlChWcV;+78IE@81a(rBO1~g^mE+OY^Ef$) zkLo@Wpa99c1{A0lZ-MlU;aXj>Utp}x7r&M2H_*b5KxP0I>dsm0sHdLK7Sh6k<>__cvjuRH@|UOoZ;*ALjgSvV?71! zQrA)vk~IFoPyGIw=bp{FQ?n0n~CH>}t15h&T z1r#&>TBQIt-m(*#U~%K?Z*MPz@wHQ5iJv!LyG^=%%}-Rj&i$pu=g*w>XLo9vr_wN% zO_)UnINR{PuFdaH;P}%|Joa~gQDuw`Yc8Z3S*i_mEphB#EVh-I)^NS{QG2Z5lCi|z zdemnuG0r6!#%;*Aha0GzHlt2=xh}dT_gYPcSgqvBH>vH2V;g^Tj#UZtvYB6a z_QZ`h@4WHmo$1HQ*NOn(?3vR?j~t9?-n;ugOP(Jb4LnrH}pX+qUj7Ife+X#OP zvjD2l@yt zzl>(cShg5C7?(#Vs2K$Ev8UHws|$@nLo4|srJ$7G3H0>H1f=dY=#vEo$%*^1YnlO4 zv~T|tTZ0$;rw{CX&`y^^`pV3v zW)w`LlxS@!rl)ap%@CWTZE36{uNb@;F%ytrM zkb$u4+#Ch+*O`!R8z#=uf<5>jErW3O%;|%Fdw(NmecaEUIsN3B)AiylleQ=da>ft6 zCa3~-l-%4`YYhRsaO%WDAx-S3BJL&JBmN&U+ zrz_V4M z3zs2vz>uLCMHpiC~4NW@wzceW_Fa8-e`u$R0g{euJf%td{xvY_&^^S9ZzDr4=1GD;bE4xzQVI+Rw( z==&W>^7kMxkyB9QU4K^aN|2xyCW)-t1q=Wd6=FG8U3;)f$n%&G8i3@Wu9KzreT7<= zBEgM^oP(!21jAS!3U{q&Pg8ZxzqoKnR-z^|RKP76bY>Lnn3+Q02675SaEcX?3%6K} z>u%}8@iN2N4*5L?V@z3k|3GWqs=el0L81%GJzy04Yrng2+4122Wfdr@ghdByd0DQN zuN_fLV&7D)3mE2!QLyGahHc8P zS!8r_5eqX$&Yyl3)F>GKA`|%&`oSca%>=-KKweKsH<4qN;x6rgGRMLW7>-7R!9bqN z(p(;jg`mlZ@sN`>cd4){1p4l=vr7h{ zp`+*4byq2+{naQ+oRj@NHwSE^s@(XHRwjDq2yO~$cIAP6`Bkr+ox4Jwzx~2DzWuG| zE^WL31`Mzq3_Mft7dQM6QV>H-)MzaF&fl^4VZW6KHxM{vg1mb*V@cd&bDJ zaXE)c5v#SfBmoC@>dmB1s23dji%|<#R@Qu`5R6Oi_DtQrnsuazHq+BFaQARVE_O+ znE(J6aIDP$pa1{>32;bRa{vGf5&!@T5&_cPe*6Fc00(qQO+^RR3=9?!HX24>ApigX z6?8>dbVg}xWgt#rZDjyeZggpMY-MCEGBTm}P@Vt)02p*dSaefwW^{L9a%BKPWN%_+ zAW&#;bZ>KLZ*Vlrj%NS>03mcmSaer%X>?_B08@2vWpYqXM<8N(AVP9wZe(F@AVP0! zY-MwF`}WZQ03ZNKL_t(|oTR@<3ZvokwfE=9O9 zFMQz(|Kk-|A%zqnmoqXw(~Frt(C9`tjzZmeM8@G}rhG8>2#?4tbPMT{C`5R;o12-c zseXEy%b%U&;EvP(34j~t##uO*&ZV<(U-@cHN?2q%ACndf?6otdpIyWbaxnD(7nGJ808h+ANj{F|7+f4X2;}?QfOx4vG^eHSWHC- zgtxH=U{G+-1fYDw<_Z62m;X?hzN=l|&Pw}p2DlfM1*O_@aaS>TfZaOqq-3uoahoegyEg<=#_ zi4d?>7Ix3UAwhCFNKS$b$X1r}-M;@?HyHX1M^JRrUU6TIsi6!;po=mb6Gq1ls? zI&9<2MS&n1xN!hqejoJ@`ljLJpS+UMYUzJE;G`A4h81NEOT5FK;s6rvd{emgQn0l% z5kMfGY5lZJex7(zn2Ck6#PQ@xf(Ubq5et}M44l-##_33FFNxB*C8G6R0Wc;P;(>TK z@Rpba!jy1lVQv~2L!nLb$eqkdyZ)TlAruSF!@!!d|J+a9Qe?X z5oXwXSPKU0(T(*;U0I%#Wv6UNE61>zs!VE!IU~W^*-$!9 zYs%V7B0_6Se0aoTW9|8>??Fn0|Ilz@gd@V+XoBKEe){D~xhvEo&k85v)WTfhT+Jz* ziB@RfBnBQb?l}YjfEhGmS=Txi1x^HaSi#vIRdgR$jcImpQg;@;A{gDTDeytycexF4 zE($b}_(R+v;rj#jZ3hCHGGEYA7LXl80bwm%IXBLv0lnT?Kvj`%xD>C-BC4CT69B+uQ_-|A6EkrtPQ?t~Hl~KdZ&UcE)ODDVvQMs@3kb*9Idp^ODfbU~n2pGPXm}{5 zq7iXK#REC-`)ls*l5NG2>~$eQbZh83ngzI1me@!S-qj%6hFrVH-BpFV+anaZ&*%~h z=a)IZ^4W=)2@{$_M4Mex1Bfzrjbd*WF{^|3n$`%aUqTj35(bR>;xJ#_4F%EZ-^>uH^U@e7YLMV%m-bu|Iqtn zLF7XNAtyj{hhyIK;h^0rw~l2%h6s(4XkHXKqHEiD;~d(d;a7h(PB>8xJ`sn8v12-lJNx7Exk2RMTcvBztg(!q%X<8BNtv5 z{-19B!T~e!j?q++c6?|kjF0}SVG~RoD~gn`1e2=qMG)7{2I30i&_umQjv8(~iYZIV zC4P+;+$p%bD)+313CXZ=zjZA=ui127kOizEPSu`3V?=t3TPUai*(sDnOjs^u4G}vU z^bC;UWhpmaVDh8D&jM2sjL@PvOk7i`fw%*CRb_e_=GM@RV{xkT+yN6@Iu51aw`h8Z zF5Y0ZL~fjmipq8G6@TtwFqK4FdwU`Z){2$0g5r3GriZGeKFmAg~><8-$YErhWH7p%BU{mj60-z2wi2Z=CtD6+NJh7quR20vuF#S<0GrO6OXtXY|BKpPgFQzlA8^D7V_kdi($K*J|LkMirz0_g*G>U* zYsrOh>$u}sT{DhvIT0_Qa~LGnOx=|?x6T7+^ELK_@Bp0w?l&q|__`NP#e1y%vT)r4ro}Y{ zkES@mCbaWGAT_-}Dn==kf@N@G#*(x4wD1Tah#6t7=_BKjcxW68)jwRQIPN?)-ZFlh zNzi)jIjzc#hg-)X{s3A?-{%e4o17|%0}^wXDJ?x#lmF6rSL93lH$6gC(@QKnLl4xe z)Lp}oo2o$}{QuB{^~M%XWG|frEfz=+7EW^F0!#S49*Z|ONF6Pd??~U0onPnf=V`O{ z^B)*Ljl5%+5zh$k_2YcgBp$-d3dtw5Vy-BWMzh?qO5WU7L&ND3`8nYQj@(%lUOEfs zdNAMC-mNH(HCUtRqX8r}z2~n$N|cOku;Q`M^zH!fvD8eQ%08AZ#6!a>8yPl7bJT9_ zVbPF(N7?`!DH`OFH618aR8$!)?T!5&C{oWG6v_&}CO@MTPOXPo0W)#sxRV?$dT>^5 z5lDn$4y2qq822%l#Pylc$F zvhaG~q|3q*I`>R*iczBM44cR*mq04?j_iWL#Ap}@-N3ObPNI>YOUFWnkWJA9&Cv|C zBhR4?3cY|4KgBZqV!=(w6arL}d7e@`CiN_szO<<*bPY z&^GXOX6-b?5DXeIg@?vFMt%;rgpZu4LH$K;om(f4RnPa(X8-2{)MhY~8Jjin2*4V# zc9u@5+Hi`}VKWC-VLy)%FO+BGZ^^JI{30*DNb^U=Uqn7Og0NHKd(ift{Wl$|?hWmo zp-^5^UN~qbwWJlXa~e^Q(HvX%vc!e!U!m%t|g`3t&#t)Mn{9vC2UP;XIOIgdJ5 zBVXS$A-iKkeo1;paom0BdhR?ee4W>~#KGRoiPe`mC4kA4CMacYZ~ehL#;1Xg1Dl1% zUlTt>XHX!=eByhk!XK-FftD)>soKB6@~oZh@G*WP_4 z3I#UswCInSXP#GHz>JhA?xld!PBcFjk#6BB6OGdoEnb%SKRVaWUj&iyKM>z>ds|ps zt$qHo@B_O~Rt*k_evlgVZ2!ad##RLN5~N@gW%sbG`2AL12EH0QskUphD(?mU9eLrU z=(Y3f%#%!1_?yT_fmXx`@dv$EOr>X|yVx9DN#3cC#NFbn9&Q(B>AZHH6rPpk3vVOV z;;od1u^b7RT|iTD3_vMP@>sA)&qh-*mrv28S8Y8TNvMGaMbx4Rv^!4Px}K_ysZp$L zSh4PdopsMt7eETk#F2RA-k~7eF+xjT5U#+NPCD=u4zfEyA$_G%aQHm)W$s>j%9U{5 zwhHH>5iJ~vMx2Nfk-T~7e4f`gZlZ0pUZH=@?MvrpfyclTg(cyGSPI#OLpo%& zyK88BW-GwX!~`y#4jQ+H1ffE|vc%G!bkf#yR-+#)@)Pnkx$vs!tHKwhTYB?>@oDHG zZSxlP&JZf84!!NXS4Y5_yn;2p(w38|gS-CS?(#|Uk|Np~;q)rOeXF`7o*?z1-#ypB6cck9 zsQTZ18}smxoZ|AX*9kqZ(!qjWxdWPBQVYw&KyUKjNpe(apt z0y|q<#@ts*FUhaS?l(n$Ec`LwzVZ;5d>Z&!q^&spV{G2Lsd`RIeH^!n zrP5If#kn1VBv;PLDX?@_(0P|hKqH%(9b>KbTzKO_ZQy0uT$b6Vkxv6tBLwV$G=XVP z`RWR(2_%4*J`|`CCvAiOmBiZW|NYo#ok)jWek}@$;oHnA513!(N*_H!$DJ}KsCflln0J? z*ii)#?ovhQ`GxX#l)}dOD(g3S{n9sUKRFYB9r+}H5l;voAfw#kOMKmDm{c}$=hC?; z+`yIh{-n-HbWVX1U?Q8!&SXJtrDdk9o$I3N2*}QWtm=aIs<+XDc`>&W(>PV))8+?VNA5;b;H{vvUBww@b_iCkrr>jMBN)MyBMY zhee%?4cpNR$}93Wq|CDLUE#};pO(~taq@xjH<1ei5hH3zzr$BJl)`dICb=G50BhKI zDV|*kXkemvCY#8%OD#JImR>fVIxig&%&>~Y*KVa)FwTSzT;p&i<^dD-_Q*gR+3i~XuSyK0%;d57wE6`_<23@bi*UR4#uEBJ@1!3E9^2&G#22Mp`6l7t zW_@H4u}Ms7Fuw@f7fW~DLJVd_Kh8mH+mIY_B%=Dfoq|PC#BFtNtouv-lJu0aA{T#I z^qb7LW!(`WM#6i>-$ahB_o>`&c=?1&Sh{zp&$V^dBqu{LXk^E-iCLqz+DFy>xN@?W zj*}5<+UmJs ztsThtF|@khePehc{f1m%RXN;LF_TVHTW96c0Rnsw<4mpkVr2m zPKICR%?5+XN5y?oAsTm7;mG3Lc^L}iv3!mrmWnM15iHb&n<~{SD1TCGfQxCMS zowd{9LztBSt?YrUA+{rkl>3yXvi7v}k|7a|NkcOe=VC4%7;lS-`XLnORJ+7WuYYK*agvR;=d#DdCJd=8By@lz{HT#j&dagaUXWy zRbLVf;J%U0#EGniBEHVCcj{ghZifWwU0Zi%?|{|Dno$GNnu&-nhp`705voOIOY*PC zg-hql%(tbxa)a6MTmx3Fe@b-;x|5&?T5*g`6k0+vP`5sARqP!w!{!IQ=ckydf@NBZ}#@iIj)`ElbyP% z4R`3=TM2SYvw;r0(7~6PFLL+F8FE8*ofi3(rvQy?4vqFINf@%W*OiBywDY34kD@!a`ggI(;!)bBZ3Bu*3>)+>*B!0%OxuVGneFA3ApEg8Y3``Q6B z=PWQ4R)fWUkGJys5BENUt#uyTWR|-Pe(jM)F?e2iSPW?n2f2O+Q|Lp1N2bq*+;v3= zo|QiHeefQuk>3_QDJ;Y<^ZL7zI&Z7-B0Qp+L(+;Cg0_)_o?q8q60E&+u<@E7TMB?h znq$?(G)~2ZY2G`#h1?`HXpWttBNTD(2jCk3H;CAbu*SdTL>Sz{d>CWIs6{(N?h6KG zoaZu3ioWmr8xIW%QKFE(HWC64H#`B&V@wq-WPJuxW0cVf@(#IN5;AFtdvZw`9Pa)NXAVd^?r+i3Oi1sGM{!h zqtHhKS3~{USGijf=kiI#8EzAmslpezW&zlQ>9O@*-Bt>{v&0MPpx_Jlt}iCBv}5HM z2`vPeiDTm!j>UyIF}%8?2IE7g*i4$D2?#YqlGXvKs(UFzq#A%!d)BC+w~?8+t|?^` zaQqf!I0)_@PW_+as;$MiHQx*ASG?%>`^5Xkv>}|~#qW^%@G+Wme95WOx84>oDLaAK z%9pMcupHB zZHQWQ&bs&--MUso&i}2mq1KS4LY%;f)BupN7S63(ITP7qdG+LOtH(OZ(pgcKPUn_; zxL|=8&`iwWSezKg;zYDC5n({cHAoR89b+?ejOKj~kP2R$qI(p|Sf&&-!Qez8lCt;Z zyVcHZAUP#_{+-HIkHN!-=;V8HLjIJ*(&%51dHPDX9enEL1xGW{2pbHGKljk#hw6jKa3LLQ^U9;2 zTlppfy_5Pxbm~(hig3ua*1+lDRpDh}iB?`K)VfT{V3=VII#O(*kSyW= z1RgKAf*NF;7N)YrPNyi!B3wKFkQAT!d#1;c_6KO@Z=m|Y#NR#c;WXXjCMhyF>)^u0&r$N+*1 z<6OKbu{zwFR{52OUnKr2G8YTzoQ<;@4E|eZ;lA`uQlU?@wMI6lZPk0sqUiOS)Wffl zjapUyb*XhuF)CEq(+eaq7boIaHJ59*fU2%Q*c2V18JehfW3V#-cA9QH7vDcM+n*xL zu`3?P7T!mmyt)CESHAiz&EE_9w?^|0I)U_O%=i6782M?oZ6s{YaqWn(lVSPa?dRT* z9H5o8oTO$#w{|OiIgCe(6_!Wu72SW&L4FSpw;oru$lYv15q`g2Vb?+gFE*L7`B z^E9`7k0C_J$7jAz4~{4A-ifT5S$LljX@SUh#TqCWlmst* z^<6?TBeaC6+7r==W)MgLG)f;zUOLjcq@~BgEJ7d@mRBB6r3+KUfsB?;VR9jU68JFC zsvl>lYOY#vHCO4V&k-2h41+Jicjnk=BL0-TzWaOM-fp%WX@bLv6ew#4Q*n$EWWl)@ zs)YNn*B)`-6vh6YJ`6Z%i&17&>-gK^FS8y!CA@W4jNIS2O>2U6c~Z!InmZykV&mMx zG*LQ|bLGBvZQ#2cBA#8_#Psh=8rK^>@1HIp{Apq7sSxwRZCe0s5H)So^H6n%#F5ER ztC7|vElF$7oi~N~M9mz3YMBz{lOjBdP3;6zRa9@>JWnuTM7&og;icv+IxPWBcijhY!e78ikw zKG#-T4~4}9&TD7XS4P{fx%F7zd9-(cd1|Eq=p)UkzYDT2osxz{t-cBKt*6ywo@u08R zsYxFSbcyELRcX*15q>BEc^C87o$hYOwFhH{u?^(h$*niY2x*`2`||`yEldcv%oQ0p z%^^@~dBU}G0lvTKb$ zPQ=nxZ6c!gI_1{=#1ub54Xk%v+hec&>D%#^kIwG$Ec-$4D0(M7pHbsmI@Q_RA}*Vu zC38iQZe0(g9#F}X>)Lq$Wr;n84VcuTE4n9GOZWk5aYLgzz8=|cd!oUUSUn>vl^e